最短路问题——弗洛伊德算法(Floyd)
问题描述:有向带权图中,求源点到结点 X 的最短路。
弗洛伊德的思路:
1. link[ i ][ j ] 储存边权信息,path[ i ][ j ] 记录从 i 到 j 目前为止的最短路,pre[ i ][ j ] 记录目前 i 到 j 的最短路中 j 的前驱 (path初始化为INF)
2. 从源点到结点 X 的路径如果存在,那么 1 ≤ 最短路边数 ≤ n - 1 (共 n 个点)。同时,只需要枚举所有的路径长度,就可以找到最短路。(废话)
3. 如果 link[ i ][ j ]存在,把 path[ i ][ j ] 置为 link[ i ][ j ],pre[ i ][ j ] 置为 i
4. 三重循环,最外层是枚举每个点(mid)作为中转的点,内层两个循环分别是枚举起点 i 和终点 j 。这样,最外层的每一次枚举(假设是第k次)实质上就是枚举了从 i 到 j所有长度为 1 到 k+1的路径[这地方说的不清楚,日后填坑]。如果 path[ i ][ j ] > path[ i ][ mid ] + path[ mid ][ j ],说明途经 mid 的一条路长度小于当前 i 到 j 的路径的长度,则更新 path 与 pre
5. 更新pre时,要时刻注意pre的实质是当前最短路径下终点的前驱。所以pre[ i ][ j ]应该更新为pre[ mid ][ j ],而不是mid
#includeFloyd#include #include #define INF 10000 //题目说边权10000代表不连通 using namespace std; int link[105][105],a; int path[105][105],pre[105][105];//path是i到j的当前路径长度,pre是当前从i到j的路径中j的前驱 int route[105];//记录从源点到X的最短路径 void Floyd() { for(int i=0;i) for(int j=0;j) path[i][j]=INF; for(int i=0;i) for(int j=0;j) { if(link[i][j]>=0) { pre[i][j]=i;//这句别忘了 path[i][j]=link[i][j]; } } for(int mid=0;mid) for(int i=0;i) for(int j=0;j) { if(path[i][j]>path[i][mid]+path[mid][j]) { pre[i][j]=pre[mid][j]; //这需要注意,pre[i][j]不能更新为mid //一定理解pre的实际含义 path[i][j]=path[i][mid]+path[mid][j]; } } } void print(int st,int end) { if(path[st][end]==INF) { printf("NO\n"); return ; } printf("%d\n",path[st][end]); int p=end; int l=0; while(p!=st) { route[++l]=p; p=pre[st][p]; } printf("%d",st); for(int i=l;i>=1;i--) { printf(" %d",route[i]); } printf("\n"); } int main() { cin>>a; for(int i=0;i) for(int j=0;j) scanf("%d",&link[i][j]); Floyd(); int st,end; while(scanf("%d%d",&st,&end)==2) { if(st==-1) return 0; print(st,end); } return 0; }