这题难就难再要字典序输出,要是单单floyd的话,无法保证最后得到的路径字典序最小,一个简单的反例就是如果6 5 7 8 9和6 8 1 2 9以及6 10 1 2 9同时是6-9的最短路的话,如果忽略相等情况下的更新,6 8之间是不会被7作为中间节点而更新的,但是不忽略的话,又可能被中间比较大的节点更新了。所以直接floyd(采用path[i][j] = k表示i到j通过k点的方式还原路径)是不满足题意的。因此这里采用另外一种记录路径的方式,path[i][j]表示从i到j的第二个元素编号,那么由于仅仅只记录了第二个元素,因此当距离相等的时候,我们就只需要考虑新的“第二个元素”是否更优。这样可以避免将这个路径进行输出比较,如果距离相等,第二个元素相等,那么最后还原出来的路径就是一致的,无需更新。
代码如下:
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; /* 相比于其他的最短路,这里只是多了一个节点上的信息 */ int N; int G[1005][1005]; // 由于题目中并没有说清楚数据范围,因此我们可以假设最大的内存 int path[1005][1005]; // 记忆路径 int dis[1005]; // 表示距离 int add[1005]; // 表示额外的节点附加值 void floyd() { int reca[1005], recb[1005], idxa, idxb; for (int i = 1; i <= N; ++i) { for (int j = 1; j <= N; ++j) { path[i][j] = j; } // path[i][j]记录的是从i到j这条路的第二个元素 } for (int k = 1; k <= N; ++k) { for (int i = 1; i <= N; ++i) { if (i == k || G[i][k] == -1) continue; for (int j = 1; j <= N; ++j) { if (j == k || G[k][i] == -1) continue; if (G[i][k] != -1 && G[k][j] != -1) { if(G[i][j] > G[i][k] + G[k][j] || G[i][j] == -1) { G[i][j] = G[i][k] + G[k][j]; path[i][j] = path[i][k]; } else if (G[i][j] == G[i][k] + G[k][j]) { if (path[i][j] > path[i][k]) { // 由于记录的直接是第二个元素,所以只有当这个第二个元素小于已知值时才更新 path[i][j] = path[i][k]; } } } } } } } void display(int x, int y) { if (x != y) { printf("-->%d", path[x][y]); display(path[x][y], y); } } int main() { while (cin >> N, N) { memset(path, 0xff, sizeof (path)); for (int i = 1; i <= N; ++i) { for (int j = 1; j <= N; ++j) { scanf("%d", &G[i][j]); // 对整个图进行一个读取 } } for (int i = 1; i <= N; ++i) { scanf("%d", &add[i]); } for (int i = 1; i <= N; ++i) { for (int j = 1; j <= N; ++j) { if (i == j || G[i][j] == -1) continue; else G[i][j] += add[j]; } // 将节点信息直接附加在边上,这样就是起始点没有加上节点值 } floyd(); int a, b, first; while (scanf("%d %d", &a, &b), a!=-1 && b!=-1) { first = 1; printf("From %d to %d :\n", a, b); printf("Path: %d", a); display(a, b); puts(""); printf("Total cost : %d\n\n", a!=b ? G[a][b]-add[b] : 0); } } return 0; }