问题描述:  有向带权图中,求源点到另一点 X 的最短路。

 

迪杰斯特拉的思路:

  1. 所有结点分成两类:

    ①确定了源点到该点的最短路的结点 (vis == true)

    ②没有确定源点到该点的最短路的结点 (vis == false)

  2. 用 vis[i] 表示结点 i 的种类,用 dis[i] 表示源点到结点 i 的沿当前线路的距离< 在4中详细解释 '当前线路' 这个名词 >。( dis 初始成 INF ,vis 初始成 false)

   用 link[i][j] 记录边 <i, j> 的权值

  3. 易知,如果 i 结点为第①类结点,dis[i] 就是源点到该结点的最短路(废话)

  4. 由 3 知,对于所有②类结点 J ( I 为所有①类结点),如果用 min (dis[j], dis[i] + link[i][j]) 更新 dis[j],dis[j]中最小的 dis[j0] 就是源点到 j0 的最短路。至此,j0成为①类结点。而其他的更新后的 dis,是先从源点到 i,再从 i 直接到 j 的距离,两段加在一起可不一定是源点到 j 的最短路,只能说是沿当前线路的距离。

  5. 由 3, 4 知,更新不用每次枚举所有的 I, J,而只需要考虑最新加入的一个①类结点 i0 与 J 的上述关系。这是因为,除了 i0 外的关系已经在之前的更新中被更新了。

  6. 最开始只有一个①类结点,源点(废话)。此时,dis[源点] = 0(又是废话)。已找到源点到结点 X 的最短路<=> X 已经是①类结点了(还是废话)。至此,可以愉快地写代码了。

 

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a,link[505][505];
int route[505],dis[505],minroute[505]; //route[i]为 ①类结点 i 的前驱,minroute记录最短路径 
bool vis[505];


void print(int st,int end)
{
    int p=end,l=0;
    while(p!=st)
    {
        minroute[++l]=p;
        p=route[p];
    }
    printf("%d\n%d",dis[end],st);
    for(int i=l;i>=1;i--)
    {
        printf(" %d",minroute[i]);
    }
    printf("\n");
}
void dijistra(int begin,int st,int end) //找到从 begin 到 end 的最短路,并记录最短路径 
{//st就是最新的 ①类结点 
    vis[st]=1;
    if(st==end)  //此时,已经找到了从 begin到 end的最短路 
    {
        print(begin,end);
        return ;
    }
    for(int i=0;i<a;i++)
    {
        if(dis[i]>link[st][i]+dis[st] && !vis[i]) //更新dis,并且记录 i结点沿当前路径的前驱 
        {
            route[i]=st;
            dis[i] = link[st][i] + dis[st];
        }
    }
    int minpos = 10000,mid;  //这里赋10000是因为题目中说边权10000代表不连通。偷个懒,不想判断连通性了 
    for(int i=0;i<a;i++)       //找到 j0 
    {
        if(vis[i]) continue;
        if(dis[i]<minpos)
        {
            minpos=dis[i];
            mid=i;
        }
    }
    if(minpos==10000)  //这意味着目前所有 ②类结点的dis都大于等于10000了,它们都不与源点连通
                       //这说明 X 不与源点连通 
    {
        printf("NO\n");
        return ;
    }
    else dijistra(begin,mid,end); //更新 i0 (代码中的 mid)与剩余 ②类结点的关系 
}
int main()
{
    scanf("%d",&a);
    for(int i=0;i<a;i++)
        for(int j=0;j<a;j++)
        {
            scanf("%d",&link[i][j]);
        }
    int st,end;
    while(scanf("%d%d",&st,&end)==2)
    {
        if(st==-1)
        {
            return 0;
        }
        memset(vis,0,sizeof(vis));
        for(int i=0;i<a;i++) dis[i]=10000;
        dis[st] = 0;
        dijistra(st,st,end);
    }
    return 0;
}
Dijkstra