Dijkstra算法-松弛边寻找一个点到任意点的最短距

floyd -w 是求任意点到任意点的最短距离,因为复杂度较高,所以在只求一个点到任意点的距离的时候容易超时,Dijkstra算法就是解决一个点到任意点的距离的问题,可以用于有向图或者是无向图,只需要注意初始化就好,于floyd-w的算法一样用临界矩阵存储图,我们还需要一个一位数组来存储原点(1)到任意点的距离

《Dijkstra算法-松弛边寻找一个点到任意点的最短距》《Dijkstra算法-松弛边寻找一个点到任意点的最短距》

然后依次对dis中最小的数的点开始寻找结点然后开始依次松弛

《Dijkstra算法-松弛边寻找一个点到任意点的最短距》

《Dijkstra算法-松弛边寻找一个点到任意点的最短距》

动态图为

《Dijkstra算法-松弛边寻找一个点到任意点的最短距》

对于下图没有出边所以代码中i<n而不是i<=n

《Dijkstra算法-松弛边寻找一个点到任意点的最短距》

但是dijkstra并不能解决带负权值的图,如以下数据

无向图

1 2 2

1 3 3

3 2 -2


代码

<span style="font-size:14px;">#include <iostream>
#include <cstring>
using namespace  std;
int map[1000][1000];
int visit[1000];
int dis[1000];
int inf=99999999;
int main()
{
    int n,m,a,b,c,i,j,k,u=0;
    int min;
    cin>>n>>m;
    //地图的初始化
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(i==j)
            {
                map[i][j]=0;
            }
            else
                map[i][j]=inf;
        }
    }
    for(i=1;i<=m;i++)
    {
        cin>>a>>b>>c;
        map[a][b]=c;
    }
    //控制是从第几个顶点到任意顶点的最短距离,下面的是1,把该点到所有点的值放入dis数组中
    for(i=1;i<=n;i++)
    {
        dis[i]=map[1][i];
    }
    memset(visit,0,sizeof(visit));
    visit[1]=1;
    //进行最短路程选择
    for(i=1;i<n;i++)//这块要注意
    {
        min=inf;
        for(j=1;j<=n;j++)//在dis中选择一个离原点最近的点,并记录他的坐标
        {
            if(visit[j]==0 && dis[j]<min)
            {
                min=dis[j];
                u=j;
            }
        }
        visit[u]=1;
        for(k=1;k<=n;k++)//然后寻找是否有于该点相连的其它点
        {
            if(map[u][k]<inf)//如果有就交换
            {
                if(dis[k]>dis[u]+map[u][k])
                {
                    dis[k]=dis[u]+map[u][k];
                }
            }
        }
        
    }
    for(i=1;i<=n;i++)//输出从原点到任意点的最短距离
    {
        cout<<dis[i]<<' ';
    }
}
//6 9
//1 2 1
//1 3 12
//2 3 9
//2 4 3
//3 5 5
//4 3 4
//4 5 13
//4 6 15
//5 6 4</span>

关于dij算法的路径输出,因为每一次它松弛的边并不是随意选的边,而是选的原点到该点最近的点的路径的边,所以每一次松弛过的边的起始点,就肯定是最短路径要经过的点,用一个pre数组来存储这些数字,在存储的时候判断pre里最后一个数和它相不相等

<span style="font-size:14px;">#include <iostream>
using namespace std;
int map[1000][1000];
int dis[1000];
int visit[1000];
int inf=99999999;
int pre[1000];
int main()
{
    int i,j,k=0;;
    int n,m,t1,t2,t3,min,u = 0;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            if(i==j)
                map[i][j]=0;
            else
                map[i][j]=inf;
        }
    }
    for(i=1;i<=m;i++)
    {
        cin>>t1>>t2>>t3;
        map[t1][t2]=t3;
    }
    for(i=1;i<=n;i++)
    {
        dis[i]=map[1][i];
    }
    dis[1]=0;
    memset(visit,0,sizeof(visit));
    visit[1]=1;
    pre[k++]=1;//注意存最开始数字
    for(i=1;i<n;i++)
    {
        min=inf;
        for(j=1;j<=n;j++)
        {
            if(visit[j]==0 && min>dis[j])
            {
                min=dis[j];
                u=j;
            }
        }
        visit[u]=1;
        for(j=1;j<=n;j++)
        {
            if(map[u][j]< inf)
            {
            if(dis[j]>dis[u]+map[u][j])
            {
                dis[j]=dis[u]+map[u][j];
                if(pre[k-1]!=u)
                  pre[k++]=u;
            }
                
            }
        }
    }
    pre[k]=6;//注意存最末尾数字
    for(i=0;i<=k;i++)
        cout<<pre[i]<<' ';
}</span>

多最短线路问题

对于普通的dij算法来说它输出的最短路径过程,只适用于单一的最短路径,如果被其它条件例如相同路径下选取时间花费最少的这种条件一束缚,便不能输出正确答案,而它输出的责是dis数组中每一轮选出的最小的,例如a-2-c-1-f 和a-1-d-2-f以上代码他会选择后者

但是题里面往往有一种条件来限制你在所有最短路径中选取一个满足条件的,hd3790就是,附上代码来自:http://blog.csdn.net/cambridgeacm/article/details/7831535

这个题是多最短路径选择的问题,条件是在路径最短的境况下选择花费最少的,很典型基础的题,这个代码很好,赞

这个代码的思想如下,首先在用矩阵存储图的时候要注意重边问题,而这道题处理了,对于题中另一个限制变量花费,代码中用了map和cost来记录每条线路的距离和花费,用

dis和value来记录原点到各个点的距离和花费,然后就是如何判断走完了一条最短的路后,判断它是不是最短路里面最省钱的呢,我自己是这么理解的,假如说三个点被线连着,第一个点也连着第三个点,因为dij对边进行松弛的时候,总是选择在dis数组里面离远点最近的而且没松弛过的,所以从最短路径来说,这个点肯定是最短路径里的点了,但是如果加了花费这个条件的话,则和它走123路程相同而且更省钱的方法就是1直接到3,所以当我们判断如果dis[1-3]==dis[1-2-3]的话,我们就开始判断它们的花费的大小。

<span style="font-size:14px;">#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
#define INF 0x7fffffff
int n,m;
int map[1005][1005];
int cost[1005][1005];
void dijkstra(int st,int ed)
{
    int i,j,v,Min;
    int visit[1005],dis[1005],value[1005];
    for(i=1;i<=n;i++)
    {
        dis[i]=map[st][i];
        value[i]=cost[st][i];
    }
    memset(visit,0,sizeof(visit));
    visit[st]=1;
    for(i=1;i<n;i++)
    {
        Min=INF;
        for(j=1;j<=n;j++)
            if(!visit[j]&&dis[j]<Min)
            {
                v=j;
                Min=dis[j];
            }
        visit[v]=1;
        for(j=1;j<=n;j++)
        {
            if(!visit[j]&&map[v][j]<INF)
            {
                if(dis[j]>dis[v]+map[v][j])
                {
                    dis[j]=dis[v]+map[v][j];
                    value[j]=value[v]+cost[v][j];
                }
                else if(dis[j]==dis[v]+map[v][j])
                {
                    if(value[j]>value[v]+cost[v][j])
                        value[j]=value[v]+cost[v][j];
                }
            }
        }
    }
    printf("%d %d\n",dis[ed],value[ed]);
}
int main()
{
    int i,j,st,ed;
    int a,b,c,d;
    while(scanf("%d%d",&n,&m),n||m)
    {
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                map[i][j]=INF;
                cost[i][j]=INF;
            }
        while(m--)
        {
            scanf("%d%d%d%d",&a,&b,&c,&d);
            if(map[a][b]>c)//这个地方是为了防止重边
            {
                map[a][b]=map[b][a]=c;
                cost[a][b]=cost[b][a]=d;
            }
            else if(map[a][b]==c)//就像是随然起始点和目的地相同,但是它们走过的路程不同,这里取最近的一条路线
            {
                if(cost[a][b]>d)
                    cost[a][b]=cost[b][a]=d;
            }
        }
        scanf("%d%d",&st,&ed);
        dijkstra(st,ed);
    }
    return 0;
}
</span>

---------


    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/superna666/article/details/52948753
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞