中级篇——Dijkstra算法求最短路径

        在讲Bellmen算法时提到过Dijkstra算法,其在处理稠密图时的时间优于Bellmen算法而且可以处理双向图,但缺点是不能处理带有负边权值的路径问题。

        Dijkstra算法需要定义一个二维数组e[n][n](n表示一共n个点),每个单元存放一个值,e[i][j]表示i点到j点的距离,e[i][i]的点距离均设为0(自己到自己距离为0),还需要一个一维数组dis[n],来记录起始点到每个点的最短路径,输出时输出dis[终点]即可,此外还需要定义一个vis[n]数组用来统计每个点是否用到过。


        现在给出几条边的值,求a到b点的最短路径:

        1     2     1

        1     3     12

        2     3     9

        2     4     3

        4     3     4

        4     5     13

        5     6     4

        4     6     15

        3     5     5

        根据所给数据填入二维数组e中

      1     2    3    4     5     6 
1     0     1    12  max   max   max
2    max    0    9    3    max   max
3    max   max   0   max    5    max
4    max   max   4    0     13    15
5    max   max  max  max    0     4
6    max   max  max  max   max    0

        以试求1到6的最短路径为例,将1设置为源点。初始化dis[]数组以源点所在行为基准

               dis        1       2        3        4        5       6

                             0      1       12     max   max   max

               vis         1       2        3        4        5       6

                             1       0        0        0        0        0

第一步:寻找除1点外离源点最近的点并求以这个点为起点的边到其他个点的距离。可见,现在2点离1点最近,看以2为起点的边2——>3,2——>4。目前dis[3]=12,而若从1先到2再从2到3距离则为dis[2]+e[2][3]=10<dis[3]!因此更新dis[3]的值。dis[4]现在为max,从1到2再到4距离为dis[2]+e[2][4]<dis[4],因此更新dis[4]!得到新的dis[]数组为:

                dis        1        2        3        4        5        6 

                              0       1        10      4      max   max

               vis         1       2        3        4        5       6

                             1       1        0        0        0        0

第二步:再寻找3,4,5,6中距离源点最近的点。此时4离源点最近还按照刚才的做法4——>3,4——>5,4——>6。dis[3]更新为dis[4]+e[4][3]=8;dis[5]=dis[4]+e[4][5]=17;dis[6]=dis[4]+e[4][6]=19。此时数组变为:

                dis        1        2        3        4        5        6

                              0        1        8        4       17     19

               vis         1       2        3        4        5       6

                             1       1        0        4        0        0

第三步:从3,5,6中找离源点最近的点为3。3出发3——>5。dis[5]>dis[3]+e[3][5],dis[5]更新为13。

                dis        1        2        3        4        5        6

                              0        1        8        4       13      19

               vis         1       2        3        4        5       6

                             1       1        1        1        0        0

第四步:5,6中5距离原点更近,5出发5——>6,dis[6]>dis[5]+e[5][6],dis[6]变为17。

                dis        1        2        3        4        5        6

                              0        1        8        4       13      17

               vis         1       2        3        4        5       6

                             1       1        1        1        1        0

第五步:只剩6了,从6无出发路线故无法操作,dis[]最终定为:

                dis        1        2        3        4        5        6

                              0        1        8        4      13      17

               vis         1       2        3        4        5       6

                             1       1        1        1        1        1

输出1——>6的距离为dis[6]=17。 


代码实现:

#include <iostream>
#include<stdio.h>
#define Max 9999999
using namespace std;
int dis[1005],vis[1005];
int e[1005][1005];
int n,m,a,b,u,v,w,Min;
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0) break;
        for(int i=1;i<=n;i++)
        {
            vis[i]=0;
            for(int j=1;j<=n;j++)
            {
                e[i][j]=Max;
            }
        }
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&w);
            if(e[u][v]>w)
            {
                e[u][v]=e[v][u]=w;
            }
        }
        scanf("%d%d",&a,&b);
        vis[a]=1;
        for(int i=1;i<=n;i++)
        {
            dis[i]=e[a][i];
        }
        for(int i=1;i<=n-1;i++)
        {
            Min=Max;
            for(int j=1;j<=n;j++)
            {
                if(vis[j]==0&&dis[j]<Min)
                {
                    Min=dis[j];u=j;
                }
            }
            vis[u]=1;
            for(v=1;v<=n;v++)
            {
                if(e[u][v]<Max)
                {
                    if(dis[v]>dis[u]+e[u][v])
                    {
                        dis[v]=dis[u]+e[u][v];
                    }
                }
            }
        }
        printf("%d %d\n",dis[b],value[b]);
    }
    return 0;
}

来个训练题——hdu3790

给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
 

Input
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)
 

Output
输出 一行有两个数, 最短距离及其花费。
 

Sample Input
  
  
3 2 1 2 5 6 2 3 4 5 1 3 0 0
 

Sample Output
  
  
9 11
相较于上面所讲,这题只需要添加一个计算花费的数组并每次更新

#include <iostream>
#include<stdio.h>
#define Max 9999999
using namespace std;
int dis[1005],vis[1005],value[1005];
int e[1005][1005],p[1005][1005];
int n,m,a,b,u,v,w,c,Min;
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0) break;
        for(int i=1;i<=n;i++)
        {
            vis[i]=0;
            for(int j=1;j<=n;j++)
            {
                e[i][j]=Max;p[i][j]=Max;
            }
        }
        while(m--)
        {
            scanf("%d%d%d%d",&u,&v,&w,&c);
            if(e[u][v]>w)
            {
                e[u][v]=e[v][u]=w;p[u][v]=p[v][u]=c;
            }
            else if(e[u][v]==w&&p[u][v]>c)
                p[u][v]=p[v][u]=c;
        }
        scanf("%d%d",&a,&b);
        vis[a]=1;
        for(int i=1;i<=n;i++)
        {
            value[i]=p[a][i];dis[i]=e[a][i];
        }
        for(int i=1;i<=n-1;i++)
        {
            Min=Max;
            for(int j=1;j<=n;j++)
            {
                if(vis[j]==0&&dis[j]<Min)
                {
                    Min=dis[j];u=j;
                }
            }
            vis[u]=1;
            for(v=1;v<=n;v++)
            {
                if(e[u][v]<Max)
                {
                    if(dis[v]>dis[u]+e[u][v])
                    {
                        dis[v]=dis[u]+e[u][v];value[v]=value[u]+p[u][v];
                    }
                    if(dis[v]==dis[u]+e[u][v])
                    {
                        if(value[v]>value[u]+p[u][v])
                            value[v]=value[u]+p[u][v];
                    }
                }
            }
        }
        printf("%d %d\n",dis[b],value[b]);
    }
    return 0;
}




   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值