Djikstra算法求解最短路径问题

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int INF=0x3f3f3f3f;
int mp[1001][1001];
int d[1001];
bool vis[1001];
vector<int> ans;
int n,m,st,ed;
void Djikstra(int st)
{
    memset(vis,0,sizeof(vis));
    int i;
    for(i=1;i<=n;i++)
    {
        d[i]=INF;
    }
    d[st]=0;
    while(true)
    {
        int v=-1;
        //从集合以外的点里面找到距离起点最近的点
        for(i=1;i<=n;i++)
        {
            if(vis[i]==false&&(v==-1||d[i]<d[v]))
            {
                v=i;
            }
        }
        //说明所有的点都在集合内 此时跳出循环
        if(v==-1)
        {
            break;
        }
        //将v加入集合
        vis[v]=true;
        //用新加入集合的点v更新与其相邻的点距离起点的最短路径
        //之所以不更新集合内的点是因为集合内的点的d的值已经是最小的了
        for(i=1;i<=n;i++)
        {
            if(vis[i]==false)
            {
                d[i]=min(d[i],d[v]+mp[v][i]);
            }
        }
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&st,&ed);
    int i,j;
    for(i=1;i<=n;i++)
    {
        for(j=i;j<=n;j++)
        {
            mp[i][j]=INF;
            mp[j][i]=INF;
        }
    }
    for(i=0;i<m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        mp[a][b]=min(c,mp[a][b]);
        mp[b][a]=mp[a][b];
    }
    Djikstra(st);
    printf("%d\n",d[ed]);
    //找出行走路线
    int now=ed;
    ans.push_back(now);
    while(true)
    {
        if(now==st)
        {
            break;
        }
        for(i=1;i<=n;i++)
        {
            if(mp[now][i]<INF&&d[now]-mp[now][i]==d[i])
            {
                ans.push_back(i);
                now=i;
                break;
            }
        }
    }
    int len=ans.size();
    for(i=len-1;i>=0;i--)
    {
        if(i<len-1)
            printf(" ");
        printf("%d",ans[i]);
    }
    printf("\n");
    return 0;
}

找出最短路径的长度是多少:

设一个集合set,vis[i]=true表示顶点i在set中,vis[i]=false表示顶点i不在set中。

设一个数组d,d[i]表示顶点i与起点之间的最短距离。

起初将vis都初始化为false,将d都初始化为无穷大INF。

起点为st,将d[st]设为0,表示起点st到它自己的最短路径长度是0。

每次都从set之外的顶点中选取一个距离起点最近的顶点v,将其加入set;

利用顶点v更新与顶点v相邻的set之外的顶点i与起点之间的最短距离d[i]。

直到所有顶点都已加入set,此时跳出循环。

找出最短路径经过的点:

第一种算法(自己想出来的):

最短路径一定是最短路径更新出来的

设数组ans为最终结果。从终点ed开始往前找,与ed相邻的顶点i,只有当ed的最短路径长度的d[ed]减去ed与i之间的距离mp[i][ed]是i的最短路径长度d[i]时,d[ed]才可能是从d[i]更新得到的,将i加入ans。之所以说“可能”,是因为最短路径可能不只一条,但ans中最终存储的结果一定是最短路径之一

同理,一直找到起点st跳出循环。

此时ans中的元素从后往前找就是最短路径依次经过的顶点。

第二种算法(更为普遍通用的算法):

开一个数组pre[n],存储最短路径中当前结点的前一个结点,最后由终点一步一步找到起点

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int INF=0x3f3f3f3f;
int mp[1001][1001];
int d[1001];
bool vis[1001];
int pre[1001];
vector<int> ans;
int n,m,st,ed;
void Djikstra(int st)
{
    memset(vis,0,sizeof(vis));
    int i;
    for(i=1;i<=n;i++)
    {
        d[i]=INF;
    }
    d[st]=0;
    while(true)
    {
        int v=-1;
        //从集合以外的点里面找到距离起点最近的点
        for(i=1;i<=n;i++)
        {
            if(vis[i]==false&&(v==-1||d[i]<d[v]))
            {
                v=i;
            }
        }
        //说明所有的点都在集合内 此时跳出循环
        if(v==-1)
        {
            break;
        }
        //将v加入集合
        vis[v]=true;
        //用新加入集合的点v更新与其相邻的点距离起点的最短路径
        //之所以不更新集合内的点是因为集合内的点的d的值已经是最小的了
        for(i=1;i<=n;i++)
        {
            if(vis[i]==false)
            {
                if(d[v]+mp[v][i]<d[i])
                {
                    d[i]=d[v]+mp[v][i];
                    pre[i]=v;
                }
            }
        }
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&st,&ed);
    int i,j;
    for(i=1;i<=n;i++)
    {
        for(j=i;j<=n;j++)
        {
            mp[i][j]=INF;
            mp[j][i]=INF;
        }
    }
    for(i=0;i<m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        mp[a][b]=min(c,mp[a][b]);
        mp[b][a]=mp[a][b];
    }
    Djikstra(st);
    printf("%d\n",d[ed]);
    //找出行走路线
    int now=ed;
    while(true)
    {
        ans.push_back(now);
        if(now==st)
        {
            break;
        }
        now=pre[now];
    }
    int len=ans.size();
    for(i=len-1;i>=0;i--)
    {
        if(i<len-1)
            printf(" ");
        printf("%d",ans[i]);
    }
    printf("\n");
    return 0;
}
发布了84 篇原创文章 · 获赞 210 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/weixin_41676881/article/details/100065204