最短路径-Dijkstra+Floyd+Spfa
Dijkstra算法:
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。
问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径。(单源最短路径)
算法的基本思想是:每次找到离源点(上面例子的源点就是 1 号顶点)最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。基本步骤如下:
- 将所有的顶点分为两部分:已知最短路程的顶点集合 P 和未知最短路径的顶点集合 Q。最开始,已知最短路径的顶点集合 P 中只有源点一个顶点。我们这里用一个vis[i]数组来记录哪些点在集合 P 中。例如对于某个顶点 i,如果vis[i]为 1 则表示这个顶点在集合 P 中,如果 vis [i]为 0 则表示这个顶点在集合 Q 中。
- 设置源点 s 到自己的最短路径为 0 即 dis=0。若存在源点有能直接到达的顶点 i,则把 dist[ i ]设为 e[s][i]。同时把所有其它(源点不能直接到达的)顶点的最短路径为设为 ∞。
- 在集合 Q 的所有顶点中选择一个离源点 s 最近的顶点 u(即 dist[u]最小)加入到集合 P。并考察所有以点 u 为起点的边,对每一条边进行松弛操作。例如存在一条从 u 到 v 的边,那么可以通过将边 u->v 添加到尾部来拓展一条从 s 到 v 的路径,这条路径的长度是 dist[u]+e[u][v]。如果这个值比目前已知的 dist[v]的值要小,我们可以用新值来替代当前 dist[v]中的值。
- 重复第 3 步,如果集合 Q 为空,算法结束。最终 dist 数组中的值就是源点到所有顶点的最短路径。
Floyd算法:
Floyd算法又称为插点法,理解起来也很方便,复杂度为o(n3),如要从结点1到结点n,可以由1直通n,再逐渐向其中插入其他结点作为中转点,不断更新在插入结点后的最短路径。
代码也很简洁,四行的算法。
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) e[j][k]=min(e[j][i]+e[i][k],e[j][k]);
Spfa:
SPFA的思路比较简单,网上的说法也比较统一,NOCOW和百度百科上都有。这里在网上找到讲的比较通俗易懂的:
*SPFA(Shortest Path Faster Algorithm) *是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。 算法大致流程是用一个队列来进行维护。 初始时将源加入队列。 每次从队列中取出一个元素, 并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。 直到队列为空时算法结束。 它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。
SPFA 在形式上和BFS非常类似,不同的是BFS中一个点出了队列就不可能重新进入队列,但是SPFA中 一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本 身被改进,于是再次用来改进其它的点,这样反复迭代下去。
判断有无负环:如果某个点进入队列的次数超过V次则存在负环(SPFA无法处理带负环的图)。
其实吧...Spfa老被卡...最好还是用dijkstra吧
HDU-3790 最短路径(模板题)
Dijkstra版:
这个算法还能利用堆和优先队列进行优化
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef unsigned long long ull; 5 #define INF 0X3f3f3f3f 6 const ll MAXN = 1e3 + 7; 7 const ll mod = 1e9 + 7; 8 //权值为正 9 int n, m; 10 int vis[MAXN]; 11 int dist[MAXN]; 12 int a[MAXN][MAXN]; 13 void Dijkstra(int x,int y) 14 { 15 for (int i = 1; i <= n; i++) 16 { 17 dist[i] = a[x][i]; 18 vis[i] = 0; 19 } 20 vis[x] = 1; 21 int p; 22 for (int i = 1; i <= n; i++) 23 { 24 int minn = INF; 25 for (int j = 1; j <= n; j++) 26 { 27 if (!vis[j] && dist[j] < minn) 28 { 29 minn = dist[j]; 30 p = j; 31 } 32 } 33 vis[p] = 1; 34 for (int j = 1; j <= n; j++) 35 if (!vis[j] && dist[p] + a[p][j] < dist[j]) 36 dist[j] = dist[p] + a[p][j];//更新这个找到的距离最小的点所连的点的距离 37 } 38 } 39 int main() 40 { 41 ios::sync_with_stdio(false); 42 while (cin >> n >> m && n && m) 43 { 44 memset(a,INF,sizeof(a)); 45 for (int i = 0; i < m; i++) 46 { 47 int x, y, len; 48 cin >> x >> y >> len; 49 a[x][y] = len; 50 a[y][x] = len; //无向图 51 } 52 Dijkstra(1,n); 53 cout << dist[n] << endl; 54 } 55 }
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef unsigned long long ull; 5 #define INF 0x3f3f3f3f 6 const ll MAXN = 1e3 + 7; 7 const ll MOD = 1e9 + 7; 8 const double pi = acos(-1); 9 struct node 10 { 11 int v, c; 12 node(int a = 0, int b = 0) { v = a, c = b; } 13 bool operator<(const node &a) const 14 { 15 if (c == a.c) 16 return v < a.v; 17 else 18 return c > a.c; 19 } 20 }; 21 struct Edge 22 { 23 int v, cost; 24 Edge(int _v = 0, int _cost = 0) { v = _v, cost = _cost; } 25 }; 26 vector<Edge> G[MAXN]; 27 bool vis[MAXN]; 28 int dist[MAXN]; 29 //点的编号从1开始 30 void Dijkstra(int n, int start) 31 { 32 memset(vis, false, sizeof(vis)); 33 for (int i = 1; i <= n; i++) 34 dist[i] = INF; 35 priority_queue<node> que; 36 while (!que.empty()) 37 que.pop(); 38 dist[start] = 0; 39 que.push(node(start, 0)); 40 node temp; 41 while (!que.empty()) 42 { 43 temp = que.top(); 44 que.pop(); 45 int u = temp.v; 46 if (vis[u]) 47 continue; 48 vis[u] = true; 49 for (int i = 0; i < G[u].size(); i++) 50 { 51 int v = G[temp.v][i].v; 52 int cost = G[u][i].cost; 53 if (!vis[v] && dist[v] > dist[u] + cost) 54 { 55 dist[v] = dist[u] + cost; 56 que.push(node(v, dist[v])); 57 } 58 } 59 } 60 } 61 void addedge(int u, int v, int w) 62 { 63 G[u].push_back(Edge(v, w)); 64 } 65 void init(int n) 66 { 67 for (int i = 1; i <= n; i++) 68 G[i].clear(); 69 return; 70 } 71 int main() 72 { 73 ios::sync_with_stdio(false); 74 cin.tie(0); 75 cout.tie(0); 76 int n, m; 77 while (cin >> n >> m && n && m) 78 { 79 init(n); 80 for (int i = 0; i < m; i++) 81 { 82 int x, y, len; 83 cin >> x >> y >> len; 84 addedge(x, y, len); 85 addedge(y, x, len); 86 } 87 Dijkstra(n, 1); 88 cout << dist[n] << endl; 89 } 90 return 0; 91 }
Floyd版:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef unsigned long long ull; 5 #define INF 0X3f3f3f3f 6 const ll MAXN = 1e3 + 7; 7 const ll mod = 1e9 + 7; 8 //可处理权值为负的情况 9 int n,m; 10 int e[MAXN][MAXN]; 11 void floyd() 12 { 13 for(int i=1;i<=n;i++) 14 for(int j=1;j<=n;j++) 15 for(int k=1;k<=n;k++) 16 e[j][k]=min(e[j][i]+e[i][k],e[j][k]); 17 } 18 int main() 19 { 20 while(cin>>n>>m&&n&&m) 21 { 22 memset(e,INF,sizeof(e)); 23 for(int i=0;i<m;i++) 24 { 25 int a,b,c; 26 cin>>a>>b>>c; 27 e[a][b]=c; 28 e[b][a]=c; 29 } 30 floyd(); 31 cout<<e[1][n]<<endl; 32 } 33 return 0; 34 }
Spfa:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef unsigned long long ull; 5 #define INF 0x3f3f3f3f 6 const ll MAXN = 1e5 + 7; 7 const ll MOD = 1e9 + 7; 8 const double pi = acos(-1); 9 int dist[MAXN]; 10 bool vis[MAXN]; 11 int pre[MAXN]; //pre[x]记录的是起点为x的链表之首在数组p的位置(相当于头插法) 12 int n, m; 13 struct Edge 14 { 15 int to; //边的终点 16 int w; //边的权值 17 int pre; //同一个起点的上一个边在数组E里的位置 18 Edge(int _to = 0, int _w = 0, int _pre = 0) { to = _to, w = _w, pre = _pre; } 19 } E[MAXN]; //边的数目 20 void Spfa() 21 { 22 queue<int> q; 23 int g, j; //起点为1, 终点为end 24 int start = 1; 25 int end = n; 26 for (int i = 1; i <= n; i++) 27 dist[i] = INF; 28 dist[start] = 0; 29 q.push(start); 30 vis[start] = 1; 31 while (!q.empty()) 32 { 33 g = q.front(); 34 q.pop(); 35 vis[g] = 0; 36 for (j = pre[g]; j != -1; j = E[j].pre) 37 { 38 if (dist[E[j].to] > E[j].w + dist[g]) //能够进行松弛 39 { 40 dist[E[j].to] = E[j].w + dist[g]; 41 if (!vis[E[j].to]) //该节点v不在序列中 42 { 43 vis[E[j].to] = 1; 44 q.push(E[j].to); 45 } 46 } 47 } 48 } 49 return; 50 } 51 int main() 52 { 53 ios::sync_with_stdio(false); 54 cin.tie(0); 55 cout.tie(0); 56 while (cin >> n >> m && n + m) 57 { 58 memset(pre, -1, sizeof(pre)); 59 int id = 0; 60 for (int i = 0; i < m; i++) 61 { 62 int x, y, len; 63 cin >> x >> y >> len; 64 E[id].to = y; 65 E[id].w = len; 66 E[id].pre = pre[x]; 67 pre[x] = id; 68 id++; 69 //双向 70 E[id].to = x; 71 E[id].w = len; 72 E[id].pre = pre[y]; 73 pre[y] = id; 74 id++; 75 } 76 Spfa(); 77 cout << dist[n] << endl; 78 } 79 return 0; 80 }//未判负环(如果某个点进入队列的次数超过V次则存在负环
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef unsigned long long ull; 5 #define INF 0X3f3f3f3f 6 const ll MAXN = 1e3 + 7; 7 const ll mod = 1e9 + 7; 8 int e[MAXN][MAXN]; //邻接矩阵 9 bool vis[MAXN]; //标记数组 10 int dist[MAXN]; //源点到顶点i的最短距离 11 int path[MAXN]; //记录最短路的路径 12 int enqueue_num[MAXN]; //记录入队次数 13 int n; //顶点数 14 int m; //边数 15 int s; //源点 16 bool SPFA() 17 { 18 memset(vis, 0, sizeof(vis)); 19 memset(enqueue_num, 0, sizeof(enqueue_num)); 20 for (int i = 1; i <= n; i++) 21 { 22 dist[i] = INF; 23 path[i] = s; 24 } 25 queue<int> Q; 26 Q.push(s); 27 dist[s] = 0; 28 vis[s] = 1; 29 enqueue_num[s]++; 30 while (!Q.empty()) 31 { 32 int u = Q.front(); 33 Q.pop(); 34 vis[u] = 0; 35 for (int v = 1; v <= n; v++) 36 { 37 if (e[u][v] != INF) //u与v直接邻接 38 { 39 if (dist[u] + e[u][v] < dist[v]) //松弛操作 40 { 41 dist[v] = dist[u] + e[u][v]; 42 path[v] = u; 43 if (!vis[v]) 44 { 45 Q.push(v); 46 enqueue_num[v]++; 47 if (enqueue_num[v] >= n)//说明存在负环 48 return false; 49 vis[v] = 1; 50 } 51 } 52 } 53 } 54 } 55 return true; 56 } 57 /* 到某点的最短path及最短path的len */ 58 void Print() 59 { 60 for (int i = 1; i <= n; i++) 61 { 62 if (i != s) 63 { 64 int p = i; 65 stack<int> st; 66 cout << "顶点 " << s << " 到顶点 " << p << " 的最短路径是: "; 67 while (s != p) //路径顺序是逆向的,所以先保存到栈 68 { 69 st.push(p); 70 p = path[p]; 71 } 72 cout << s; 73 while (!st.empty()) //依次从栈中取出的才是正序路径 74 { 75 cout << "--" << st.top(); 76 st.pop(); 77 } 78 cout << " 最短路径长度是:" << dist[i] << endl; 79 } 80 } 81 } 82 int main() 83 { 84 while (cin >> n >> m && n && m) 85 { 86 s=1; 87 for (int i = 1; i <= n; i++) 88 for (int j = 1; j <= n; j++) 89 e[i][j] = INF; //初始化e数组 90 int u, v, w; 91 for (int i = 0; i < m; i++) 92 { 93 cin >> u >> v >> w; 94 e[u][v] = w; 95 e[v][u] = w; 96 } 97 if (SPFA()) 98 cout<<dist[n]<<endl; 99 else 100 cout << "Sorry,it have negative circle!\n"; //存在负环 101 } 102 103 return 0; 104 }