最短路的dijkstra算法,主要使用与无负权值边图中单源路径最小值计算,主要思想是,以源点为起点,贪心(选取离源点最近的未处理的点)扩充源点所在的点集,并以找到的点为起点继续操作,直到到达目标点。这期间通过dist[i]数组记录源点到第i个点的最短路,到函数结束后,源点到图中所有点的最短路就都在dist[]数组中了。
注意,这里和最小生成树中的prim算法比较相似,都是贪心扩充点集,但区别在于这里因为要求路径最小,所以贪心时的标准是待处理点到源点的最小值,而prim中的标准是待处理点到点集的最小值。
上代码:
#include<stdio.h> #include<string.h> #define find_min(a,b) a<b?a:b const int MAX = 0xfffffff; const int N = 210; int n,m,map[N][N],dist[N],vis[N]; void getmap() { int i,j,a,b,c; memset(vis,0,sizeof(vis)); for(i=0;i<n;i++) { for(j=0;j<n;j++) map[i][j] = (i==j?0:MAX); } for(i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&c); map[a][b]=map[b][a]=find_min(map[a][b],c); } } void dij(int start,int to) { int i,cur,next; for(i=0;i<n;dist[i++]=MAX); dist[start]=0; cur=start; while(1){ vis[cur]=1; int min=MAX; for(i=0;i<n;i++){ if(vis[i])continue; if(dist[i]-map[i][cur]>dist[cur]) dist[i]=map[i][cur]+dist[cur]; if(dist[i]<min){ min=dist[i]; next=i; } } cur=next; if(cur==to)break; if(min==MAX)break; } printf("%d\n",dist[to]<MAX?dist[to]:-1); } int main() { int sp,ep; while(~scanf("%d%d",&n,&m)) { getmap(); scanf("%d%d",&sp,&ep); dij(sp,ep); } return 0; }
然后是最短路的Floyd算法,这个就简单得多了,就是在整个图中扫描,看点 i 到 j 的距离和(点 i 到点 k 的距离)+(点 k 到点 j 的距离)两者哪个较小,把小的存入map[i][j]中即可。Floyd算法优势在于可以处理负边权的图,而且函数计算的是图中任意两点间的最短路;但其效率不高,空间开销较大,对于密集点图较为实用。
代码如下:
#include<stdio.h> #include<string.h> #define find_min(a,b) a<b?a:b #define MAX 0xfffffff int n,m,map[201][201],dist[201],vis[201]; void getmap() { int i,j,a,b,c; for(i=0;i<n;i++){ for(j=0;j<n;j++) map[i][j]=(i==j?0:MAX); } for(i=0;i<m;i++){ scanf("%d%d%d",&a,&b,&c); map[a][b]=map[b][a]=find_min(map[a][b],c); } } void floyd(int sp,int ep){ for(int k=0;k<n;k++) { for(int i=0;i<n;i++) for(int j=0;j<n;j++) map[i][j]=find_min(map[i][j],map[i][k]+map[k][j]); } printf("%d\n",map[sp][ep]<MAX?map[sp][ep]:-1); } int main() { int s,e; while(~scanf("%d%d",&n,&m)) { getmap(); scanf("%d%d",&s,&e); floyd(s,e); } return 0; }