HDU 1874 最直接的最短路径问题

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1874

Problem Description
某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰。
现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。
 
Input
本题目包含多组数据,请处理到文件结束。 每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。 接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。 再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。
 
Output
对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.
 
在这道题中因为数据量不大,所以用四种最短路径的方法都可以对它进行求解,也用这道题来令自己熟悉一下四种最短路径的算法:
 
Dijkstra:
 1 #include <cstdio>
 2 #include <algorithm>
 3 #include<queue>
 4 #include<cstring>
 5 using namespace std;
 6 typedef pair<int,int> pii;
 7 #define N 205
 8 #define M 1005
 9 #define MAXN 0x3f3f3f3f
10 int y[M],d[M],next[M];
11 int first[N],dp[N];
12 int k;
13 
14 //写完函数后这两句话老是忘记写,所以还是这样一开始就写在一个函数里这样自己就不会忘了
15 void init()
16 {
17     k=0;
18     memset(first,-1,sizeof(first));
19 }
20 void add(int a,int b,int c)
21 {
22     y[k]=b,d[k]=c,next[k]=first[a];
23     first[a]=k;
24     k++;
25 }
26 
27 void dijkstra(int src)
28 {
29     priority_queue<pii,vector<pii>,greater<pii> > q;
30     memset(dp,0x3f,sizeof(dp));
31     dp[src]=0,q.push(make_pair(0,src));
32     while(!q.empty()){
33         while(!q.empty()&&dp[q.top().second]<q.top().first) q.pop();
34         if(q.empty()) break;
35         int u=q.top().second;
36         q.pop();
37         for(int i=first[u];i!=-1;i=next[i]){
38             if(dp[y[i]]>dp[u]+d[i]){
39                 dp[y[i]]=dp[u]+d[i];
40                 q.push(make_pair(dp[y[i]],y[i]));
41             }
42         }
43     }
44 }
45 
46 int main()
47 {
48     int n,m,a,b,c,s,t;
49     while(scanf("%d%d",&n,&m)!=EOF){
50         init();
51         for(int i=0;i<m;i++){
52             scanf("%d%d%d",&a,&b,&c);
53             add(a,b,c);
54             add(b,a,c);
55         }
56         scanf("%d%d",&s,&t);
57         dijkstra(s);
58         if(dp[t]<MAXN) printf("%d
",dp[t]);
59         else printf("%d
",-1);
60     }
61 
62     return 0;
63 }
View Code
SPFA:
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <queue>
 5 using namespace std;
 6 #define MAXN 20010
 7 #define N 205
 8 int v[MAXN],d[MAXN],next[MAXN],first[N],visit[N],dp[N];
 9 int k;//k表示路的条数
10 
11 void add(int x,int y,int a)//这里添加的是无向图的边,所以进行两次
12 {
13     v[k]=y;
14     next[k]=first[x];
15     d[k]=a;
16     first[x]=k;
17     k++;
18     v[k]=x;
19     next[k]=first[y];
20     d[k]=a;
21     first[y]=k;
22     k++;
23 }
24 
25 int spfa(int a,int b)
26 {
27     memset(dp,0x3f,sizeof(dp));
28     //memset(visit,0,sizeof(visit));//这一段是没有必要的,每次spfa做完,他都会最后变为0
29     queue<int> q;
30     dp[a]=0,visit[a]=1;
31     q.push(a);
32     while(!q.empty()){
33         int c=q.front();
34         q.pop();
35         visit[c]=0;
36         for(int i=first[c];i!=-1;i=next[i]){
37             if(dp[v[i]]>dp[c]+d[i]){
38                 dp[v[i]]=dp[c]+d[i];
39                 if(!visit[v[i]]) q.push(v[i]),visit[v[i]]=1;
40             }
41         }
42     }
43     if(dp[b]<0x3f3f3f3f) return dp[b];
44     else return -1;
45 }
46 
47 int main()
48 {
49     int n,m,start,End,x,y,a;
50     while(scanf("%d%d",&n,&m)!=EOF){
51         k=0;
52         memset(next,-1,sizeof(next));
53         memset(first,-1,sizeof(first));
54         for(int i=0;i<m;i++){
55             scanf("%d%d%d",&x,&y,&a);
56             add(x,y,a);
57         }
58         scanf("%d%d",&start,&End);
59         printf("%d
",spfa(start,End));
60     }
61     return 0;
62 }
View Code


Floyd:

在使用Floyd时应该把矩阵每个点一开始做好初始化,主对角线上均为0;

其他定位一个最大值。

PS:这道题比较坑的地方是两地间可以有多条路,我们要判断是否为较小的路放入矩阵中

floyd是基于建立在2维矩阵中的,每次更新出一个到达 i 的最短路径,都要遍历一次矩阵,把所有其他节点到 i 点最小值不断更新出来,因为这道题城镇数目比较少,可以采取这种

复杂度为O(n^3)的方法,但是通过这个方法我们可以确定任意一点到其他点的最短路径(自我感觉类似于打表法,有木有?!),不像SPFA做一次只能找到你所需的最短路径

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 #define N 205
 7 #define MAXN 0x3f3f3f3f
 8 int mat[N][N];
 9 
10 void Floyd(int n)//为n*n的矩阵
11 {
12     for(int i=0;i<n;i++){
13         for(int j=0;j<n;j++){
14             for(int k=0;k<n;k++){
15                 if(mat[j][k]>mat[j][i]+mat[k][i])
16                     mat[j][k]=mat[j][i]+mat[k][i];//i只是用来计更新次数的,实际上每更新一次,都要将整个矩阵的所有点都遍历一遍
17                 }                                 //所以是mat[j][k];
18         }
19     }
20 }
21 int main()
22 {
23     int n,m,start,End,x,y,a;
24     while(scanf("%d%d",&n,&m)!=EOF){
25         memset(mat,0x3f,sizeof(mat));
26         for(int i=0;i<n;i++) mat[i][i]=0;
27         for(int i=0;i<m;i++){
28             scanf("%d%d%d",&x,&y,&a);
29             a=min(a,mat[x][y]);
30             mat[x][y]=a,mat[y][x]=a;//在这里要判断一下,因为两地之间可以有多条路,我们需要判断它到底是否为我们要的最短路
31         }
32         scanf("%d%d",&start,&End);
33         Floyd(n);
34         if(mat[start][End]<MAXN) printf("%d
",mat[start][End]);
35         else printf("-1
");
36     }
37     return 0;
38 }
View Code


BellMan-ford:

在写BellMan时,没必要写first[]数组了

它执行一次只能找到固定对应的a到b的最短距离,在这一点上是远远不如Floyd的,而且复杂度为O(n*k),在数据量特别大时是不适用的

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 #define N 205
 7 #define M 20005
 8 #define MAXN 0x3f3f3f3f
 9 int u[M],v[M],d[M],k;
10 int dp[N];
11 void add(int x,int y,int a)
12 {
13     u[k]=x,v[k]=y,d[k]=a;
14     k++;
15 }
16 void BellMan(int n,int src)
17 {
18     memset(dp,0x3f,sizeof(dp));
19     dp[src]=0;
20     for(int i=0;i<n;i++)
21     {
22         for(int j=0;j<k;j++)
23             if(dp[v[j]]>dp[u[j]]+d[j])
24                 dp[v[j]]=dp[u[j]]+d[j];
25     }
26 }
27 int main()
28 {
29     int n,m,start,End,x,y,a;
30     while(scanf("%d%d",&n,&m)!=EOF){
31         k=0;
32         for(int i=0;i<m;i++){
33             scanf("%d%d%d",&x,&y,&a);
34             add(x,y,a);
35             add(y,x,a);
36         }
37         scanf("%d%d",&start,&End);
38         BellMan(n,start);
39         if(dp[End]<MAXN) printf("%d
",dp[End]);
40         else printf("-1
");
41     }
42     return 0;
43 }
View Code
原文地址:https://www.cnblogs.com/CSU3901130321/p/3873601.html