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 }
例題: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)
缺點:不能處理負權邊
貝西在田野里,她想回到谷倉,在農夫約翰早上叫醒她擠奶之前盡可能多睡一會兒。貝西需要美容覺,所以她想盡快回來。農夫約翰的田里有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 }
注意: 如果是多組輸入,要清空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的時間復雜度,不好准確估計,一般認為是 O(kE),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 }
仍以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 }
鏈接表代碼:

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 }