最短路算法(floyed+Dijkstra+bellman-ford+SPFA)
最短路算法簡單模板
一.floyed算法
首先對於floyed算法來說就是最短路徑的動態規劃解法,時間復雜度為O(n^3) 適用於圖中所有點與點之間的最短路徑的算法,一般適用於點n較小的情況。
Floyed算法有三層循環,循環的層次先後順序也是比較重要的,分別為k ,i,j;因為dis[k][i][j]代表的是i節點到j節點的最短路如果中間經過節點k的話dis[k][i][j] =dis[k-1][i][k]+dis[k-1][k][j];否則dis[k][i][j] = dis[k-1][i][j];所以說我們要求第k個節點的話就必須先把所有的k-1求出來。而此處的三維dis數組正如背包問題一樣優化為二維數組。
對於任意兩個節點i,j來說;要想從節點i到達節點j的話有兩種情況:
- 由i直接到達j
- 由i經過若幹個k節點到達j
所以dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
HDU 2544 最短路
題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=2544
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 constView Codeint INF = 0x3f3f3f; 6 int n, m, mp[110][110]; 7 void floyed() 8 { 9 for (int k = 1; k <= n; k++) 10 for (int i = 1; i <= n; i++) 11 for (int j = 1; j <= n; j++) 12 mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]); 13 } 14 int main() 15 { 16 ios::sync_with_stdio(false); 17 while ((cin >> n >> m)&&n&&m) { 18 memset(mp, INF, sizeof(mp)); 19 for (int a, b, c, i = 0; i < m; i++) { 20 cin >> a >> b >> c; 21 mp[a][b] = mp[b][a] = min(mp[a][b], c); 22 } 23 floyed(); 24 cout << mp[1][n] << endl; 25 } 26 return 0; 27 }
二.Dijkstra算法
關於Dijkstra算法(貪心)的推斷過程我就不詳細的講啦,我會的別人都已經講完了而且還比我詳細,在這裏向大家推薦一篇寫的不錯的博客!https://www.cnblogs.com/nigang/p/3658990.html
這位博主講的還是很詳細的,圖解也很清晰明了
下面我還是通過一個例題來講解代碼吧!(同floyed例題)
鄰接表的Dijkstra:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int INF = 0x3f3f3f; 6 int n, m, mp[110][110]; 7 int dis[110], vis[110]; 8 void Dijkstra() 9 { 10 for (int i = 1; i <= n; i++) { 11 vis[i] = 0; dis[i] = mp[1][i]; 12 } 13 for (int i = 1; i <= n; i++) { 14 int cnt = INF, k; 15 for (int j = 1; j <= n; j++) { 16 if (!vis[j] && dis[j] < cnt) { 17 cnt = dis[j]; 18 k = j; 19 } 20 } 21 vis[k] = 1; 22 for (int j = 1; j <= n; j++) { 23 if (!vis[j] && dis[j] > dis[k] + mp[k][j]) 24 dis[j] = dis[k] + mp[k][j]; 25 } 26 } 27 } 28 int main() 29 { 30 ios::sync_with_stdio(false); 31 while ((cin >> n >> m)&&n&&m) { 32 memset(mp, INF, sizeof(mp)); 33 for (int a, b, c, i = 0; i < m; i++) { 34 cin >> a >> b >> c; 35 mp[a][b] = mp[b][a] = min(mp[a][b], c); 36 } 37 Dijkstra(); 38 cout << dis[n] << endl; 39 } 40 return 0; 41 }View Code
堆優化後的Dijkstra
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<bitset> 5 #include<vector> 6 #include<queue> 7 8 using namespace std; 9 const int INF = 1 << 30; 10 const int maxn = 110; 11 struct node{ 12 int to, cost; 13 node() {} 14 node(int a, int b) :to(a), cost(b) {} 15 bool operator<(const node&a)const { 16 if (cost == a.cost)return to < a.to; 17 return cost > a.cost; 18 } 19 }; 20 vector<node>e[maxn]; 21 int n, m, dis[maxn]; 22 void Dijkstra(int s) 23 { 24 for (int i = 1; i <= n; i++)dis[i] = INF; 25 dis[s] = 0; 26 priority_queue<node>Q; 27 Q.push(node(s, dis[s])); 28 while (!Q.empty()) { 29 node t = Q.top(); Q.pop(); 30 for (int i = 0; i < e[t.to].size(); i++) { 31 int tmp = e[t.to][i].to; 32 if (dis[tmp] > t.cost + e[t.to][i].cost) { 33 dis[tmp] = t.cost + e[t.to][i].cost; 34 Q.push(node(tmp, dis[tmp])); 35 } 36 } 37 } 38 } 39 int main() 40 { 41 ios::sync_with_stdio(false); 42 while ((cin >> n >> m)&&(n&&m)) { 43 for (int i = 1; i <= n; i++)e[i].clear(); 44 for (int a, b, c, i = 0; i < m; i++) { 45 cin >> a >> b >> c; 46 e[a].push_back(node(b, c)); 47 e[b].push_back(node(a, c)); 48 } 49 Dijkstra(1); 50 cout << dis[n] << endl; 51 } 52 return 0; 53 }View Code
三.bellman-ford算法
當路徑當中出現負權值邊的時候Dijkstra算法將不再適用,因為dijkstra由於是貪心的,每次都找一個距源點最近的點,然後將該距離定為這個點到源點的最短路徑,但如果存在負權邊,那就有可能先通過並不是距源點最近的一個次優點,而是這一個負權值邊,所求出的結果可能就不是最短距離了。
算法講解https://blog.csdn.net/niushuai666/article/details/6791765
模板:無向圖。有向圖的話邊只需要加一次即可
1 #include<iostream> 2 #include<algorithm> 3 4 using namespace std; 5 const int INF = 1 << 28; 6 const int maxn = 10005; 7 struct node { 8 int from, to, cost; 9 node() {} 10 node(int a, int b, int c) :from(a), to(b), cost(c) {} 11 }e[maxn<<1]; 12 int n, m, dis[110]; 13 bool bellman_ford() 14 { 15 for (int i = 2; i <= n; i++)dis[i] = INF; 16 dis[1] = 0; 17 for (int i = 1; i < n; i++) { 18 int flag = 0; 19 for (int j = 1; j <= 2 * m; j++) { 20 if (dis[e[j].to] > dis[e[j].from] + e[j].cost) { 21 dis[e[j].to] = dis[e[j].from] + e[j].cost; 22 flag = 1; 23 } 24 } 25 if (!flag)return true; 26 } 27 for (int j = 1; j <= m; j++) 28 if (dis[e[j].to] > dis[e[j].from] + e[j].cost) 29 return false; 30 return true; 31 } 32 int main() 33 { 34 ios::sync_with_stdio(false); 35 while ((cin >> n >> m)&&n&&m) { 36 for (int a, b, c, i = 1; i <= m; i++) { 37 cin >> a >> b >> c; 38 e[i] = node(a, b, c); 39 e[i + m] = node(b, a, c); 40 } 41 bellman_ford(); 42 cout << dis[n] << endl; 43 } 44 return 0; 45 }View Code
四. SPFA 算法<--bellman-ford算法的優化
一篇清晰的算法過程的講解!https://www.cnblogs.com/bofengyu/p/5004398.html
代碼模板如下
例題:POJ 2387
http://poj.org/problem?id=2387
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 #include<queue> 6 7 using namespace std; 8 const int maxn = 2010; 9 const int INF = 0x3f3f3f3f3f; 10 int n, m; 11 struct node{ 12 int to, cost; 13 node() {} 14 node(int a, int b) :to(a), cost(b) {} 15 }; 16 vector<node> e[maxn]; 17 int vis[maxn], f[maxn], dis[maxn]; 18 void SPFA(int s) 19 { 20 for (int i = 0; i < maxn; i++) { 21 vis[i] = 0; f[i] = 0; 22 dis[i] = INF; 23 } 24 dis[s] = 0; 25 vis[s] = 1; f[s]++; 26 queue<int>Q; 27 Q.push(s); 28 while (!Q.empty()) { 29 int t = Q.front(); Q.pop(); 30 vis[t] = 0; 31 for (int i = 0; i < e[t].size(); i++) { 32 int tmp = e[t][i].to; 33 if (dis[tmp] > dis[t] + e[t][i].cost) { 34 dis[tmp] = dis[t] + e[t][i].cost; 35 if (!vis[tmp]) { 36 vis[tmp] = 1; 37 Q.push(tmp); 38 if (++f[tmp] > n)return; 39 } 40 } 41 } 42 } 43 return; 44 } 45 int main() 46 { 47 ios::sync_with_stdio(false); 48 while (cin >> m >> n) { 49 for (int a, b, c, i = 1; i <= m; i++) { 50 cin >> a >> b >> c; 51 e[a].push_back(node(b, c)); 52 e[b].push_back(node(a, c)); 53 } 54 SPFA(1); 55 cout << dis[n] << endl; 56 } 57 return 0; 58 }View Code
最短路算法(floyed+Dijkstra+bellman-ford+SPFA)