Dijkstra算法
定义
\(
Dijkstra算法一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表的方式,这里均采用永久和临时标号的方式。注意该算法要求图中不存在负权边。
\)
原理
\(
1.首先,引入一个辅助数组(vector)D,它的每个元素D[i]表示当前所找到的从起始点(即源点v)到其它每个顶点v_i的长度。
\\
例如,D[3]=2表示从起始点到顶点3的路径相对最小长度为2。这里强调相对就是说在算法执行过程中D的值是在不断逼近最终结果但在过程中不一定就等于长度。
\\
2.D的初始状态为:若从v到v_i有弧(即从v到v_i存在连接边),则D 为弧上的权值(即为从v到v_i的边的权值);否则置D[i]为∞。
\\
显然,长度为D[j]=Min\{D|v_i∈V\}的路径就是从v出发到顶点v_j的长度最短的一条路径,此路径为(v,v_j)。
\\
3.那么,下一条长度次短的是哪一条呢?也就是找到从源点 到下一个顶点的最短路径长度所对应的顶点,且这条最短路径长度仅次于从源点v到顶点v_j的最短路径长度。
\\
假设该次短路径的终点是v_k,则可想而知,这条路径要么是(v,v_k),或者是(v,v_j,v_k)。它的长度或者是从v到v_k的弧上的权值,或者是D[j]加上从v_j到v_k的弧上的权值。
\\
4.一般情况下,假设S为已求得的从源点v出发的最短路径长度的顶点的集合,则可证明:下一条次最短路径(设其终点为x)要么是弧(v,x),或者是从源点 出发的中间只经过S中的顶点而最后到达顶点x的路径。
\\
因此,下一条长度次短的的最短路径长度必是D[j]=Min\{D[i]|∈V-S\},其中D 要么是弧( )上的权值,要么是D[k](v_k∈S)和弧(v_k,v_i)上的权值之和。
\\
算法描述如下:
\\
1)令arcs表示弧上的权值。若弧不存在,则置arcs为∞(在本程序中为MAXCOST)。S为已找到的从v出发的的终点的集合,初始状态为空集。那么,从v出发到图上其余各顶点v_i可能达到的长度的初值为D=arcs[Locate\ Vex(G,v_i )],v_i∈V;
\\
2)选择v_j,使得D[j]=Min\{D|v_i∈V-S\};
\\
3)修改从v出发的到集合V-S中任一顶点v_k的最短路径长度。
\)
问题描述
\(
在有向图G=(V,E)中,假设每条边E[i]的长度为w[i],找到由顶点V_0到其余各点的最短值。
\)
算法思想
\(
按路径长度递增次序产生算法:
\\
把顶点集合V分成两组:
\\
(1)S:已求出的顶点的集合(初始时只含有源点V0)
\\
(2)V-S=T:尚未确定的顶点集合
\\
将T中顶点按递增的次序加入到S中,保证:
\\
(1)从源点V_0到S中其他各顶点的长度都不大于从V_0到T中任何顶点的最短路径长度
\\
(2)每个顶点对应一个距离值
\\
S中顶点:从V_0到此顶点的长度
\\
T中顶点:从V_0到此顶点的只包括S中顶点作中间顶点的最短路径长度
\\
依据:可以证明V_0到T中顶点V_k的,或是从V_0到V_k的直接路径的权值;或是从V_0经S中顶点到V_k的路径权值之和。
\\
(反证法可证)
\\
求最短路径步骤
\\
算法步骤如下:
\\
G=\{V,E\}
\\
1初始时令S=\{V_0\},T=V-S=\{其余顶点\},T中顶点对应的距离值
\\
若存在(V_0,V_i),d(V_0,V_i)为(V_0,V_i)弧上的权值
\\
若不存在(V_0,V_i),d(V_0,V_i)为\infty
\\
2从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中
\\
3对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V_0到V_i的距离值缩短,则修改此距离值
\\
重复上述步骤2、3,直到S中包含所有顶点,即W=V_i为止
\)
代码
#include<iostream>
#include<climits>
#include<cstdlib>
#define MAXN 10001
using namespace std;
int a[MAXN][MAXN]={0},b[MAXN];
bool s[MAXN]={0};
int main(){
ios::sync_with_stdio(false);
int n,m;
cout<<"点数&边数"<<endl;
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
a[x][y]=z;
a[y][x]=z;
}
b[1]=0;
for(int i=2;i<=n;i++)b[i]=INT_MAX;
for(int i=1;i<=n;i++){
int minn=INT_MAX,x;
for(int j=1;j<=n;j++)
if(!s[j]&&b[j]<minn)
{
minn=b[j];
x=j;
}
s[x]=1;
for(int j=1;j<=n;j++)if(a[x][j]&&!s[j]&&b[j]>b[x]+a[x][j])b[j]=b[x]+a[x][j];
}
for(int i=1;i<n;i++)cout<<b[i]<<"->";
cout<<b[n]<<endl;
return 0;
}