最短路算法(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 const int 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 }
堆优化后的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 }
三.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 }
四. 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 }