问题描述:有向带权图中,求源点到结点 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

#include<iostream>
#include<cstdio>
#include<cstring>
#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<a;i++)
        for(int j=0;j<a;j++)
            path[i][j]=INF;
    for(int i=0;i<a;i++)
        for(int j=0;j<a;j++)
        {
            if(link[i][j]>=0)
            {
                pre[i][j]=i;//这句别忘了 
                path[i][j]=link[i][j];
            }
        }
    for(int mid=0;mid<a;mid++)
        for(int i=0;i<a;i++)
            for(int j=0;j<a;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<a;i++)
        for(int j=0;j<a;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;
}
Floyd