在讲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
(1<n<=1000, 0<m<100000, s != t)
3 2 1 2 5 6 2 3 4 5 1 3 0 0
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;
}