HDU 2544 最短路
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=2544
题意:有n个城市,若干个城市中互相有通路,每条通路的距离不同。要你从点1到点N的最短距离。输入0 0时结束。
典型的最短路题目。
Floyd
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 typedef long long ll; 6 const int maxn=105; 7 int dis[maxn][maxn],n,m; 8 //dis[a][b]表示从a到b的距离; 9 void res(int x) 10 { 11 for(int i=1;i<=n;i++) 12 for(int j=1;j<=i;j++) 13 { 14 if(i==j) dis[i][i]=0; 15 else dis[i][j]=dis[j][i]=100005; 16 } 17 } 18 //初始化路径 19 void floyed(){ 20 for(int i=1;i<=n;i++) 21 for(int j=1;j<=n;j++) 22 for(int k=1;k<=n;k++) 23 if(dis[j][k]>dis[j][i]+dis[i][k]) 24 dis[j][k]=dis[j][i]+dis[i][k]; 25 } 26 // 更新从i到j的路的最小值 27 //注意要先一个节点遍觅所有的路,否则会出现缺漏 28 int main(){ 29 while(cin>>n>>m,n||m){ 30 res(n); 31 int a,b,c; 32 for(int i=1;i<=m;i++){ 33 cin>>a>>b>>c; 34 dis[a][b]=dis[b][a]=c; 35 //更新路径,此处为双向路径 36 } 37 floyed(); 38 cout<<dis[1][n]<<endl; 39 } 40 return 0; 41 }
易理解,但效率低。
Dijsktra
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 typedef long long ll; 6 const int N=105; 7 int dis[N][N],dist[N],mark[N],n,m; 8 //dis[a][b]为a到b的距离,dist[a]为1到a的最短距离,mark[a]为a作为中转点。 9 void dijstra(){ 10 int minn,u; 11 for(int i=1;i<=n;i++) dist[i]=dis[1][i]; 12 //初始化dist数组 13 dist[1]=0; 14 mark[1]=1; 15 for(int i=1;i<n;i++){ 16 minn=100005; 17 u=0; 18 for(int j=1;j<=n;j++) 19 if(dist[j]<minn&& !mark[j]){ 20 minn=dist[j]; 21 u=j; 22 } //找出离点一最近的点 23 mark[u]=1; //标记表示已经用过 24 if(u){ 25 for(int j=1;j<=n;j++) 26 if(!mark[j]&&dis[u][j]<100005&&dist[j]>dist[u]+dis[u][j]) 27 dist[j]=dist[u]+dis[u][j]; 28 //已经作过节点的不能再用,松弛节点 29 } 30 else break; 31 } 32 } 33 //先找最近的点,松弛,再找下一个近的,松弛,一直找完为止。 34 int main(){ 35 while(cin>>n>>m,n||m){ 36 int a,b,c; 37 memset(mark,0,sizeof(mark)); 38 memset(dist,0,sizeof(dist)); 39 memset(dis,0,sizeof(dis)); 40 for(int i=1;i<=n;i++) 41 for(int j=1;j<=n;j++) 42 dis[i][j]=100005; 43 //初始化路径 44 for(int i=0;i<m;i++){ 45 cin>>a>>b>>c; 46 dis[a][b]=dis[b][a]=c; 47 } 48 dijstra(); 49 cout<<dist[n]<<endl; 50 } 51 return 0; 52 }
缺点是无法解决负环(判断有没有负环)。
Bellman_Ford
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long ll; int n,m,a[20005],b[20005],cost[20005],dist[105]; void bellman_ford(){ for(int i=1;i<=n;i++) dist[i]=100005; dist[1]=0; for(int i=1;i<=n-1;i++) //最多循环n-1次 for(int j=1;j<=2*m;j++) if(dist[b[j]]>dist[a[j]]+cost[j]) dist[b[j]]=dist[a[j]]+cost[j]; //对所有边进行松弛 } //对每条边进行重复松弛,最多n-1次,还能松弛则有负权 int main(){ int x,y,z; while(cin>>n>>m,n||m){ for(int i=1;i<=2*m;i+=2){ cin>>x>>y>>z; a[i]=x; b[i]=y; cost[i]=z; a[i+1]=y; b[i+1]=x; cost[i+1]=z; } bellman_ford(); /* for(int i=1;i<=2*m;i++) if(dist[b[i]]>dist[a[i]]+cost[i]) return false; //检测负权 */ cout<<dist[n]<<endl; } }
多了很多不必要的计算,下面有邻接矩阵的队列优化Spfa
Spfa
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 using namespace std; 6 typedef long long ll; 7 const int N=105; 8 int n,m,dis[N][N],dist[N],vis[N]; 9 //dis[a][b]是a到b的距离,dist[a]表示1到a的距离,vis记录节点 10 void spfa(){ 11 queue<int>que; 12 memset(vis,0,sizeof(vis)); 13 for(int i=1;i<=n;i++) dist[i]=100005; 14 //初始化 15 dist[1]=0; 16 que.push(1); 17 vis[1]=1; 18 while(!que.empty()){ //一直处理到队列为空为止 19 int u=que.front(); 20 que.pop(); 21 vis[u]=0; 22 for(int i=1;i<=n;i++) 23 if(dist[u]+dis[u][i]<dist[i]){ 24 dist[i]=dist[u]+dis[u][i]; //松弛 25 if(!vis[i]){ //入队 26 que.push(i); 27 vis[i]=1; 28 } 29 } 30 } 31 } 32 /*spfa为Bellman_Ford的优化,减少了不必要的重复,先放入,再拿出进行松弛, 33 若松弛成功,放入队列(不在队列),每次拿出队首松弛,直到队列为空,可重复入队*/ 34 int main(){ 35 while(cin>>n>>m,n||m){ 36 for(int i=1;i<=n;i++) 37 for(int j=1;j<=n;j++) 38 dis[i][j]=100005; 39 int a,b,c; 40 for(int i=1;i<=m;i++){ 41 cin>>a>>b>>c; 42 dis[a][b]=dis[b][a]=c; 43 } 44 spfa(); 45 cout<<dist[n]<<endl; 46 } 47 return 0; 48 }
减少了不必要的计算,但要浪费很多空间,便有了SPFA二代(链式前向星)
spfa II
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 using namespace std; 6 typedef long long ll; 7 int n,m,ans,dist[105],head[105],vis[105]; 8 struct node{ 9 int v,w,next; //v是要到达的点,w为花费,next为指向下一个点 10 }edge[20005]; 11 void add(int a,int b,int c){ 12 edge[ans].v=b; 13 edge[ans].w=c; //添加新的点 14 edge[ans].next=head[a]; //ans代表第几条边 15 head[a]=ans++; //head指向最新的点(第几条边) 16 } 17 void spfa(int start){ 18 for(int i=1;i<=n;i++) 19 dist[i]=100005; 20 dist[start]=0; 21 memset(vis,0,sizeof(vis)); 22 queue<int>que; 23 que.push(start); 24 vis[start]=1; //起点入队 25 while(!que.empty()){ 26 int temp=que.front(); 27 que.pop(); 28 vis[temp]=0; 29 for(int i=head[temp];i!=-1;i=edge[i].next){ //对每个点所指向的点一一进行处理,指向-1是就没有相连的点了,即结束了。 30 int a=edge[i].v; 31 int b=edge[i].w; 32 if(dist[a]>dist[temp]+b){ 33 dist[a]=dist[temp]+b; 34 if(!vis[a]){ 35 que.push(a); 36 vis[a]=1; 37 } 38 } 39 } 40 } 41 } 42 /*head数组初始化都为-1,嗲表没有点与点相连,如果有点与它相连,那就让head 43 指向它,然后让这个点的next指向起点即-1,代表这是最后一个与头点相连的点,若有 44 新的点进来,就把它插入到两者之间,后来的指向先来的,起点指向先来的,这样,最 45 后一个点的next一定为-1,这样即可对每个边进行松弛*/ 46 int main(){ 47 int x,y,z; 48 while(cin>>n>>m,n||m){ 49 ans=1; 50 memset(head,-1,sizeof(head)); 51 for(int i=1;i<=m;i++){ 52 cin>>x>>y>>z; 53 add(x,y,z); 54 add(y,x,z); 55 } 56 spfa(1); 57 cout<<dist[n]<<endl; 58 } 59 return 0; 60 }
节省了大量空间。
Dijstra也有优化,但水平有限,还没学会,重载的还不太懂,等以后再来补全。