// 3072K 250MS Java
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class Main{
private static final int INF = 99999;
private static final int UNREACH = -1;
private static final int MAX = 100;
private static int sStokerNum;
public int[][] stokerInfo;
public void reset() {
sStokerNum = 0;
for (int i = 0; i < MAX; i++) {
for (int j = 0; j < MAX; j++) {
if (i == j) {
stokerInfo[i][j] = 0;
} else {
stokerInfo[i][j] = INF;
}
}
}
}
public void solve() {
for (int k = 1; k <= sStokerNum; k++) {
for (int i = 0; i < sStokerNum; i++) {
for (int j = 0; j < sStokerNum; j++) {
int d1 = stokerInfo[i][j];
int d2 = stokerInfo[i][k-1] + stokerInfo[k-1][j];
stokerInfo[i][j] = d1 < d2 ? d1 : d2;
}
}
}
// for (int i = 0; i < sStokerNum; i++) {
// for (int j = 0; j < sStokerNum; j++) {
// System.out.print(stokerInfo[i][j] + " ");
// }
// System.out.println("");
// }
int bestStokerId = -1;
int shortestTime = INF;
for (int i = 0; i < sStokerNum; i++) { // check ith stoker
int longestTransTime = -1;
for (int j = 0; j < sStokerNum; j++) {
longestTransTime = stokerInfo[i][j] > longestTransTime ? stokerInfo[i][j] : longestTransTime;
}
// System.out.println(i + " " + longestTransTime);
if (longestTransTime < shortestTime) { // update
bestStokerId = i;
shortestTime = longestTransTime;
}
// bestStokerId = longestTransTime < shortestTime ? i : bestStokerId;
}
if (bestStokerId == -1) {
System.out.println("disjoint");
} else {
System.out.println((bestStokerId + 1) + " " + shortestTime);
}
reset();
}
public Main() {
stokerInfo = new int[MAX][MAX];
reset();
}
public static void main(String args[]) {
Main solver = new Main();
Scanner scanner = new Scanner(System.in);
while(true) {
int stokerNum = scanner.nextInt();
if (stokerNum == 0) {
return;
}
sStokerNum = stokerNum;
for (int i = 0; i < stokerNum; i++) {
int contactNum = scanner.nextInt();
for (int j = 0; j < contactNum; j++) {
int contactId = scanner.nextInt();
int time = scanner.nextInt();
solver.stokerInfo[i][contactId-1] = time;
}
}
solver.solve();
}
}
}
一道基本的多点对之间最短路径题,用的是标准的Floyd-Warshall算法。
题目转化很简单,一读就直到想要的是什么,不过以前没搞过FW算法,花了些时间学习了下。
算导对FW讲的虽然短,但是很好,揭露了其本质是DP算法.
假设D(i, j ,k) 表示从i到j的最短路径,并且该最短路径包含的中间点不会超过k(即k之后的点都不会被涉及到,这里的k值得是对图的点的编号,谁前谁后无所谓,但必须编号来体现出前后差异)。
那么D(i, j, k+1) 的递推就可以表示了:
case1 如果第k+1个点包含在了D(i, j, k+1)的最短路径中,那么D(i,j, k+1) = D(i, k+1, k) + D(k+1, j, k)
case2 如果第k+1个点没有包含在D(i, j, k+1)的最短路径中, 那么第k+1点的加入相当于没有任何影响,因此D(i,j, k+1) = D(i, j, k).
至于是case1还是case2, 就是通过比较两种情况下哪个路径最短来决定的。
这样动态规划的递推式就是:
D(i, j, k+1) = MIN(D(i, k+1, k) + D(k+1, j, k), D(i, j, k)) (会有 k+1 ==i 或者 k+1==j的情况,这种情况, D(i, k+1, k) + D(k+1, j, k) = D(i, j, k))
有3个变化量,因此动态规划递归求解的方式是三重循环:
看一下倚赖关系: k+1 依赖于k, 因此 k的循环要从小到大,并且k从1开始,k=0表示最短路径间没有任何中间点,就是两个点之间之否有直接连接,这等于是初始状态,
这也表示k是DP中的主变量,要以k作为第一重循环.
i 虽然依赖于 k+1 (k+1可能比i大,也可能小), 但要注意,其实第k+1轮倚赖的是第k论结果,因此实际上,i从大到小或相反是不影响的,
j 同i,
i,j作为第2,3轮循环,前后无所谓。
传统的DP在这时候要开三维矩阵来保存中间值以进行DP,但是这种情况完全可以把3维压缩成2维,
第k+1的轮的结果只依赖于第k轮,之前的结果不会影响,因此完全可以在求第k+1论的结果的过程中,一边求,一边覆盖相应的的第k轮的中间结果(要注意保证不能覆盖还没有被使用的第k轮结果),这样只用一个二维矩阵就可以求了。
最后对于本题来说,就是一次查询每一个stoker对其他人是否全部可达,如果可达,求出最长的路径,如果不可达,则不予考虑,
如果下一个stoker的全部可达,且最长路径比之前的stoker中的最短的最长路径(有点绕)还要短,则更新最短的最长路径和stokerid。
最后如果没有stoker能全部可达其他人,则disjoint,否则输出stokerId和其最长的路径(就是从他开始传播到最后也是最晚一个stoker的时间)
还要注意初始化时,要给邻接矩阵合适的值,D(i,i) = 0(自己到自己当然是0)不可达(也是所有的初始状态)给一个极大值INF(自己定9999999),其他的按照input的值来。