AOJ2076 Flame of Nucleus
問題リンク Flame of Nucleus
- 概要
N個のドームと、M本の双方向の道がある。ドームにはそれぞれPi人の人と、Ki人収容可能なシェルターがある。M本の道はドームiとドームjを結んで通行にD日の時間がかかる。
L日後に核が落とされる。L日目より早くシェルターに逃げ込んだ人は助かることができる(L日目ちょうどにシェルターに入っても助からない)。このとき、助かることができる最大人数を答えよ。
ドームiとドームjを結ぶ道は高々1つと仮定してよい。
1 <= N <= 100
0 <= M
1 <= L <= 10000
1 <= Di <= 10000
0 <= Pi <= 10^6
0 <= Ki <= 10^6
- 解法
最大流問題となります。
まずは、ドームiとドームjの全ドーム間の最短距離をワーシャルフロイド法等で求めておきます。iとjの最短距離がLよりちいさいならば、ドームiの人はドームjのシェルターまで移動可能と分かります。
最大流についてです。
頂点は2*N + 2個で内訳は、[各ドームについての頂点が2個ずつ]と[ソース]と[シンク]の頂点です。各ドームについての頂点はAi, Biと区別して表すことにします。
まずソースからAiに向けて、容量Piの辺を引きます。ドームにいる人の初期人数を表しています。
Biからシンクに向けて、容量Kiの辺を引きます。シェルターの収容制限を表しています。
次に最短距離がL未満であるようなドームiとドームjについて、AiからBjへ容量Piの辺を引きます。人がiのドームからjのドームへ移動することを表しています。
あとはこのグラフの最大フローを求めて答えとします。自分はFord-Fulkersonを使っています。少し遅いみたいですけど通りました。
- ソース
import java.util.Arrays; import java.util.Scanner; //Flame of Nucleus public class AOJ2076 { int INF = 1<<29; int augumentPath(int v, int t, int f, int[][] cap, boolean[] used){ if(v==t)return f; used[v] = true; for(int i=0;i<cap[v].length;i++){ if(cap[v][i]>0 && !used[i]){ int d = augumentPath(i, t, Math.min(f, cap[v][i]), cap, used); if(d>0){ cap[v][i] -= d; cap[i][v] += d; return d; } } } return 0; } int maxFlow(int s, int t, int[][] cap){ int flow = 0; int n = cap.length; boolean[] used = new boolean[n]; while(true){ Arrays.fill(used, false); int f = augumentPath(s, t, Integer.MAX_VALUE, cap, used); if(f==0)return flow; flow += f; } } void run(){ Scanner sc = new Scanner(System.in); while(sc.hasNext()){ int n = sc.nextInt(), m = sc.nextInt(), L = sc.nextInt(); int[][] wf = new int[n+1][n+1]; for(int[]a:wf)Arrays.fill(a, INF); for(int i=1;i<=n;i++)wf[i][i]=0; while(m--!=0){ int s = sc.nextInt(), t = sc.nextInt(), c = sc.nextInt(); wf[s][t]=wf[t][s]=c; } for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)wf[i][j]=Math.min(wf[i][j], wf[i][k]+wf[k][j]); int[] p = new int[n+1], k = new int[n+1]; for(int i=1;i<=n;i++)p[i]=sc.nextInt(); for(int i=1;i<=n;i++)k[i]=sc.nextInt(); int[][] cap = new int[n*2+2][n*2+2]; for(int i=1;i<=n;i++)cap[0][i]=p[i]; for(int i=1;i<=n;i++)cap[n+i][2*n+1]=k[i]; for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(wf[i][j]<L)cap[i][n+j]=p[i]; System.out.println(maxFlow(0, 2*n+1, cap)); } } public static void main(String[] args) { new AOJ2076().run(); } }