最短路總結


Floyd算法:

 復雜度O(n^3)

首先這個算法使用暴力dp來寫的,很容易就會TLE。但是這是一個多源最短路算法,可以求出來任意兩點之間的最短距離

示例代碼:

 1 #include <cstdio>
 2 #include <iostream>
 3 #define INF 0x3f3f3f3f
 4 using namespace std;
 5 int n,m,t,a,b,c;
 6 int f[105][105];
 7 int main()
 8 {
 9     //有n個端點,m條邊 
10     cin >> n >> m >> t;
11     //初始化 
12     for (int i=1;i<=n;i++)
13         for (int j=1;j<=n;j++)
14         {
15             if (i==j) f[i][j]=0;
16             else f[i][j]=INF;
17         }
18     //輸入邊 
19     for (int i=1;i<=m;i++)
20     {
21         scanf("%d%d%d",&a,&b,&c);
22         f[a][b]=c;
23     }
24     //核心代碼 
25     for (int k=1;k<=n;k++)
26         for (int i=1;i<=n;i++)
27             for (int j=1;j<=n;j++)
28                 f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
29     //運行完算法之后,f[x][y]里面就是x與y之間的最短距離 
30     return 0;
31 }
View Code

 

例題:UVA10048

 

Dijkstra算法:

算法過程:

給你n個點m條邊,你要求x到y的最短距離。這個時候你首先用從x為起點的邊來處理一遍控制最短距離的數組dis[](數組初始化dis[x]=0,其他都是正無窮)。然后再用距離x最近的點(設為x1),用以x1為起點的邊在來處理一遍dis數組。這個時候dis[x1]里面的值就是x->x1的最短距離。原因就是在所有邊都是正數的情況下,那么肯定不可能從x通過一個第三者中轉導致x->x1的距離變得更短

 

堆優化迪傑斯特拉優化的哪個地方:沒有堆優化的時候我們用的是普通隊列,在以x為起點的時候我們對所有與x有關的邊都進行一次松弛(比如有x-->1這一條邊,我們要去判斷dis[1]與dis[x]+w[x][1],w[x][1]是x與1這一條邊的長度),所有邊進行松弛之后我們要找出來除了dis[x]之外最小的那個dis[y](這個y是我們假設的那個與x相距最小的那個點【同樣這個y也是一個未確定過的點,什么叫未確定?比如x就叫確定過的點,因為dis[x]我們已經保證它是最小的了】)

注意了:我們堆優化的就是這個找dis[y]的過程(因為暴力找需要for循環 ,而我們用優先隊列了之后就不需要這么麻煩了)

 

是一個單源最短路算法

優點:

普通算法復雜度:O(v*e),

加堆優化:在核心代碼部分,最復雜的是 while 循環和 for 循環嵌套的部分,while 循環最多循環 v 次(v 為頂點個數),for 循環執行次數與邊的數目有關,假設頂點數 v 的最大邊數是 e。
for 循環中往優先隊列中添加刪除數據的復雜度為O(log v)。
綜合上述兩部分,最終 Dijkstra 算法的時間復雜度是O(e·logv)

缺點:不能處理負權邊

例題:Til the Cows Come Home

貝西在田野里,她想回到谷倉,在農夫約翰早上叫醒她擠奶之前盡可能多睡一會兒。貝西需要美容覺,所以她想盡快回來。農夫約翰的田里有N個(2 <= N <= 1000)個界標,唯一的編號是1..地標是谷倉;貝茜整天站在里面的小樹林是地標性的N.奶牛在田野里用T (1 <= T <= 2000)在地標之間不同長度的雙向牛道。貝西對自己的導航能力不太自信,所以一旦她開始了,她總是沿着一條路線走到底。根據各地標之間的步道,確定貝西返回谷倉的最小距離。可以保證存在這樣的路由。
輸入*第一行:兩個整數:T和N
第2行 . .T+1:每一行都用三個空格分隔的整數來描述一個軌跡。前兩個整數是路線經過的地標。第三個整數是步道的長度,范圍1..100。
輸出*第1行:單個整數,貝茜從地標N到地標1的最小距離。

樣例輸入
5 5
1 2 20
2 3 30
3 4 20
4 5 20
1 5 100
樣例輸出
90

 

加堆優化+代碼:

 1 //Dijkstra迪傑斯特拉+堆優化(鄰接矩陣存圖)
 2 #include<stdio.h>
 3 #include<string.h>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<vector>
 8 using namespace std;
 9 #define MAX 0xffffff
10 struct shudui1
11 {
12     int start,value;
13     bool operator < (const shudui1 q)const
14     {
15         return value<q.value;
16     }
17 }str1;
18 struct shudui2
19 {
20     int start,value;
21 }str2;
22 int v[1005];
23 priority_queue<shudui1>r; //里面的數據默認是從小到大排序,這樣就不用通過for循環遍歷在每一次找v里面的最小值,可以直接找到最小值,減少代碼運行次數
24 int a,s,d,f,g;
25 vector <shudui2>w[2005];  //將每一個起點的值作為數組的標識
26             //例如,1 2 3這一組數據,就是說1連接着2,那就把所有1能到的地方存到這個里面
27 int main()
28 {
29     scanf("%d%d",&a,&s);
30     for(int i=1;i<=s;++i)
31         v[i]=MAX;
32     while(a--)
33     {
34         scanf("%d%d%d",&d,&f,&g);
35         str2.start=f;
36         str2.value=g;
37         w[d].push_back(str2);
38         str2.start=d;
39         str2.value=g;
40         w[f].push_back(str2);
41     }
42     v[s]=0;
43     str1.start=s;
44     str1.value=0;
45     r.push(str1);
46     while(!r.empty())
47     {
48         int x,y;
49         str1=r.top();
50         r.pop();
51         x=str1.start;
52         y=str1.value;
53         if(v[x]<y) continue;
54         //說明在這個點再此之后又入隊了
55         //此次出隊的並不是s到這個點的最短路,
56         //所以在這次更新前點v所連的點已經更過一次了
57         //所以后面也不會進行松弛操作
58         int len=w[x].size();
59         for(int i=0;i<len;++i)
60         {
61             str2=w[x][i];
62             if((v[x]+str2.value<v[str2.start]))
63             {
64                 v[str2.start]=v[x]+str2.value;
65                 str1.start=str2.start;
66                 str1.value=v[str2.start];
67                 r.push(str1);
68             }
69         }
70     }
71     printf("%d\n",v[1]);
72     return 0;
73 }
View Code

注意: 如果是多組輸入,要清空vector容器呀!!!!!!!!!(wa的我還以為是我的模板錯了。。。。)

 修正一下上面迪傑斯特拉中一部分代碼:

//Dijkstra迪傑斯特拉+堆優化(鄰接矩陣存圖)
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define MAX 0xffffff
struct shudui1
{
    int start,value;
    bool operator < (const shudui1 q)const
    {
        return value>q.value;   //這個要改成大於號
    }
}str1;

HDU-6290 奢侈的旅行 (Dijkstra+堆優化)    就例如這一道題,你用小於號就會wa

 

Spay算法(bellman-Ford優化):

 

算法過程:

給你n個點m條邊,你要求x到y的最短距離。這個時候你要枚舉每一條邊u->v,權值為w,看可不可以通過dis[v]=dis[u]+w來找到dis[v]的更小值(數組初始化dis[x]=0,其他都是正無窮)。在第一輪的松弛之后,得到的是“只經過一條邊”到達其余各頂點的最短路徑長度。第二輪的時候,得到的是“只經過二條邊”到達其余各頂點的最短路徑長度。那我們只需要經過n-1輪次的循環就可以了,因為在一個有n個頂點的圖中,任意兩點之間的距離最多包含n-1邊

 

Bellman-Ford這種寫法相當暴力, 直接循環nodeNum次, 每次枚舉每一條邊, 假如這條邊可以用於松弛源點到端點的距離, 則進行松弛. 至於判斷負環, 再枚舉一遍所有邊, 假如存在邊仍能用於松弛, 則說明存在負權回路.

 

SPFA算法的優化體現在:松弛操作必定只會發生在最短路徑前導節點松弛成功過的節點上,用一個隊列記錄松弛過的節點,可以避免了冗余計算。復雜度可以降低到O(kE),k是個比較小的系數(並且在絕大多數的圖中,k<=2,然而在一些精心構造的圖中可能會上升到很高)

 

關於SPFA的時間復雜度,不好准確估計,一般認為是 OkE),k是常數。可處理負邊,可判斷負環

 

Bellman-Ford算法代碼:POJ 3259 Wormholes

  1 #include <stdio.h>
  2 
  3 #include <string.h>
  4 
  5 #define N 505
  6 
  7 #define inf 100000
  8 
  9 int dis[N];   //最小距離 
 10 
 11 int n, m;
 12 
 13 struct Cow 
 14 
 15 {
 16 
 17     int x;//起點
 18 
 19     int y;//終點
 20 
 21     int w;//權值
 22 
 23 }cow[5300];
 24 
 25 int Bellman_ford(int m)
 26 
 27 {
 28 
 29     int i, j, k, t;
 30 
 31     int ok;
 32 
 33     for(i=1; i<=n; i++)
 34 
 35         dis[i] = inf;
 36 
 37     dis[1] = 0;
 38 
 39     //對每一個點進行松弛   盡可能的讓路徑最小 
 40 
 41     for(i=1; i<=n-1; i++)
 42 
 43     {
 44 
 45         ok = 1;
 46 
 47         for(j=1; j<=m; j++)
 48 
 49         {
 50 
 51             if(dis[cow[j].x] > dis[cow[j].y] + cow[j].w)
 52 
 53             {
 54 
 55                 dis[cow[j].x] = dis[cow[j].y] + cow[j].w;
 56 
 57                 ok = 0;
 58 
 59             }
 60 
 61         } 
 62 
 63         //如果沒有點進行更新  那么跳出即可  可減小時間復雜度 
 64 
 65         if(ok) 
 66 
 67             break; 
 68 
 69     }
 70 
 71     //判斷圖中是否有負權值存在 
 72 
 73     for(j=1; j<=m; j++)
 74 
 75     {
 76 
 77         if(d[cow[j].x] > d[cow[j].y] + cow[j].w)
 78 
 79             return 0;
 80 
 81     }
 82 
 83     return 1;
 84 
 85 }
 86 
 87  
 88 
 89 int main()
 90 
 91 {
 92 
 93     int i, j, t, x;
 94 
 95     int a, b, c, w;
 96 
 97     scanf("%d", &t);
 98 
 99     while(t--)
100 
101     {
102 
103         scanf("%d%d%d", &n, &m, &w);
104 
105         memset(edges, 0, sizeof(edges));
106 
107         //因為是無向圖  所以需要將所有的添加進去 
108 
109         for(i=0, j=1; j<=m;j++)
110 
111         {
112 
113             scanf("%d%d%d", &a, &b, &c);
114 
115             i++;
116 
117             cow[i].x = a;
118 
119             cow[i].y = b;
120 
121             cow[i].w = c;
122 
123             i++;
124 
125             cow[i].x = b;
126 
127             cow[i].y = a;
128 
129             cow[i].w = c;
130 
131  
132 
133         }
134 
135         for(j=1; j<=w; j++)
136 
137         {
138 
139             scanf("%d%d%d", &a, &b, &c);
140 
141  
142 
143             i++;
144 
145             cow[i].x = a ;
146 
147             cow[i].y = b;
148 
149             cow[i].w = -c;
150 
151  
152 
153         }
154 
155         if(Bellman_ford(i))
156 
157             printf("NO\n");
158 
159         else 
160 
161             printf("YES\n");
162 
163     }
164 
165     return 0;
166 
167 }
View Code

 

 

具體看這里

 

仍以Til the Cows Come Home 為例題:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<queue>
 4 #include<algorithm>
 5 #include<iostream>
 6 using namespace std;
 7 const int INF =0xfffff;
 8 const int MAX =2005;
 9 int w[MAX][MAX];
10 int d[MAX];
11 int vis[MAX];
12 queue<int>q;
13 bool spay(int s,int n)   //這個可以用來判斷有沒有負環的存在
14 {
15     d[s]=0;
16     int cnt=0;
17     q.push(s);
18     q.push(cnt);
19     vis[s]=1;
20     while(!q.empty())
21     {
22         int x=q.front();
23         q.pop();
24         cnt=q.front();
25         q.pop();
26         vis[x]=0;
27         if(cnt>n) return 0;  //這個是用來判斷有沒有負環,沒有負環的情況最多只用查詢其n-1個頂點,多於n個那就不正常了
28         for(int i=1;i<n;++i) //本體是從n點到其他點,所以n點不需要再回到n點,可以不遍歷
29         {
30             if(d[i]>d[x]+w[x][i])
31             {
32                 d[i]=d[x]+w[x][i];
33                 if(!vis[i])
34                 {
35                     q.push(i);
36                     q.push(cnt+1);
37                     vis[i]=1;
38                 }
39             }
40         }
41     }
42     return 1;
43 }
44 int main()
45 {
46     int n,m;
47     scanf("%d%d",&n,&m);
48     memset(w, 0x3f, sizeof(w));
49     for(int i=1;i<=m;++i)
50         d[i]=INF;
51     while(n--)
52     {
53         int a,s,d;
54         scanf("%d%d%d",&a,&s,&d);
55         if(w[a][s]>d)   //防止重邊
56         w[a][s]=d;
57         if(w[s][a]>d)
58         w[s][a]=d;
59     }
60     spay(m,m);
61     printf("%d\n",d[1]);
62     return 0;
63 }
View Code

 

鏈接表代碼:

 1 #include <stdio.h>
 2 #include<string.h>
 3 #include <iostream>
 4 #define INF 0x3f3f3f3f
 5 using namespace std;
 6 const int maxn=1005;
 7 struct edge
 8 {
 9     int u,v,w,next;    
10 }e[maxn*maxn];  //如果題目上沒有說,那就是n*n條邊,n是點 
11 int head[maxn],cnt;
12 void add_edge(int x,int y,int z){  //鏈接表 
13     e[cnt].u=x;
14     e[cnt].v=y;
15     e[cnt].w=z;
16     e[cnt].next=head[x];
17     head[x]=cnt++;
18 }
19 int main(){
20     cnt=0;
21     int x,y,z;
22     while(~scanf("%d%d%d",&x,&y,&z)){
23         add_edge(x,y,z);
24     }
25     for(int i=0;i<cnt;++i) //打印 
26     {
27         printf("%d %d %d\n",e[i].u,e[i].v,e[i].w);
28     }
29     return 0;
30 } 
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM