AOJ1058 Winter Bells

問題リンク Winter Bells

  • 解法

交差点iの子どもがサンタを見る確率は (iを通る最短経路の数)/(全最短経路の数) となります。
iを通る最短経路の数は (0からiへの最短経路数) * (n-1からiへの最短経路数) となります。
経路の数はDPで求まります。よって、2回のDPをすることで答えを出すことができます。
頂点数が100までしかないのでワーシャルフロイドをしておきます。辺(s, t)が最短経路のルートになるかどうかは、
wf[0][n-1] = wf[0][s] + cost[s][t] + wf[t][n-1]
が成り立つかどうかで判定できます。
公式解説を見ると、経路数はlongを使ってもオーバーフローするようにテストが作られているようです。doubleかBigIntegerを使えばOKらしいです。

  • ソース
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;

//Winter Bells
public class AOJ1058 {

	int[] d;
	void run(){
		Scanner sc = new Scanner(System.in);
		int INF = 1<<27;
		d = new int[100];
		int[][] wf = new int[100][100], e = new int[100][100];
		double[] K = new double[100], R = new double[100];
		for(;;){
			int n = sc.nextInt(), m = sc.nextInt(), p = sc.nextInt();
			if((n|m|p)==0)break;
			for(int[]a:wf)Arrays.fill(a, INF);
			for(int[]a:e)Arrays.fill(a, INF);
			for(int i=0;i<n;i++)wf[i][i]=0;
			for(int i=0;i<m;i++){
				int s = sc.nextInt(), t = sc.nextInt(), c = sc.nextInt();
				e[s][t] = e[t][s] = wf[s][t] = wf[t][s] = c;
			}
			for(int k=0;k<n;k++)for(int i=0;i<n;i++)for(int j=0;j<n;j++)wf[i][j]=Math.min(wf[i][j], wf[i][k]+wf[k][j]);
			PriorityQueue<Integer> q = new PriorityQueue<Integer>(n, new Comparator<Integer>() {
				public int compare(Integer o1, Integer o2) {
					return d[o1]-d[o2];
				}
			});
			Arrays.fill(K, 0);
			K[n-1] = 1;
			for(int i=0;i<n;i++){
				d[i] = -wf[0][i]; q.add(i);
			}
			while(!q.isEmpty()){
				int v = q.poll();
				for(int i=0;i<n;i++){
					if(i==v || e[i][v]==INF)continue;
					if(wf[0][n-1]==wf[0][i]+e[i][v]+wf[v][n-1]){
						K[i]+=K[v];
					}
				}
			}
			Arrays.fill(R, 0);
			R[0] = 1;
			for(int i=0;i<n;i++){
				d[i]=wf[0][i];
				q.add(i);
			}
			while(!q.isEmpty()){
				int v = q.poll();
				for(int i=0;i<n;i++){
					if(i==v || e[v][i]==INF)continue;
					if(wf[0][n-1]==wf[0][v]+e[v][i]+wf[i][n-1]){
						R[i]+=R[v];
					}
				}
			}
			while(p--!=0){
				int k = sc.nextInt();
				System.out.printf("%.8f\n", K[k]*R[k]/R[n-1]);
			}
			System.out.println();
		}
	}
	
	public static void main(String[] args) {
		new AOJ1058().run();
	}
}