Floyd 算法
//java 和 c++
for(int k=1; k<=n; k++) //floyd的三重循环
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++) // k循环在i、j循环外面
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
Dijkstra 算法
- Dijkstra:单源最短路径问题。
- 优点:非常高效而且稳定。
- 缺点:只能处理不含有负权边的图。
- 思路:贪心思想+优先队列。
-
void dijkstra(){ int s = 1; //起点s是1 bool done[N]; //done[i]=true表示到结点i的最短路径已经找到 for (int i=1;i<=n;i++) {dis[i]=INF; done[i]=false; } //初始化 dis[s]=0; //起点到自己的距离是0 priority_queue <s_node> Q; //优先队列,存结点信息 Q.push(s_node(s, dis[s])); //起点进队列 while (!Q.empty()) { s_node u = Q.top(); //pop出距起点s距离最小的结点u Q.pop(); if(done[u.id]) continue; //丢弃已经找到最短路径的结点。即集合A中的结点 done[u.id]= true; for (int i=0; i<e[u.id].size(); i++) { //检查结点u的所有邻居 edge y = e[u.id][i]; //u.id的第i个邻居是y.to if(done[y.to]) continue; //丢弃已经找到最短路径的邻居结点 if (dis[y.to] > y.w + u.n_dis) { dis[y.to] = y.w + u.n_dis; Q.push(s_node(y.to, dis[y.to])); //扩展新的邻居,放到优先队列中 pre[y.to]=u.id; //如果有需要,记录路径 } } } // print_path(s,n); //如果有需要,打印路径: 起点1,终点n }
Bellman-Ford 算法
BFS 的扩散思想,每个人都去问自己的相邻节点到
S
点的距离最近是多少。第一轮至少有一个点得到了到
S
的最短距离,即与S
相邻的节点,标记为T1
重复以上操作,那么必然至少又有一个节点找到了与
S
的最短距离,即与T1
相邻的节点,标记为T2
一共需要几轮操作?
每一轮操作,都至少有一个新的结点得到了到
S
的最短路径。所以,最多只需要n
轮操作,就能完成n
个结点。在每一轮操作中,需要检查所有m
个边,更新最短距离。Bellman-Ford 算法的复杂度:
O(nm)
。Bellman-Ford 能判断负圈:
没有负圈时,只需要
n
轮就结束。如果超过
n
轮,最短路径还有变化,那么肯定有负圈。SPFA 算法
队列优化版的 Bellman-Ford
SPFA = 队列处理+Bellman-Ford。
Bellman-Ford 算法有很多低效或无效的操作。其核心内容,是在每一轮操作中,更新所有结点到起点
S
的最短距离。 计算和调整一个结点U
到S
的最短距离后,如果紧接着调整U
的邻居结点,这些邻居肯定有新的计算结果;而如果漫无目的地计算不与U
相邻的结点,很可能毫无变化,所以这些操作是低效的。改进: 计算结点
U
之后,下一步只计算和调整它的邻居,能加快收敛的过程。 这些步骤用队列进行操作,这就是 SPFA。(1)起点
S
入队,计算它所有邻居到S
的最短距离。把S
出队,状态有更新的邻居入队,没更新的不入队。 (2)现在队列的头部是S
的一个邻居U
。弹出U
,更新它所有邻居的状态,把其中有状态变化的邻居入队列。 (3)继续以上过程,直到队列空。这也意味着,所有结点的状态都不再更新。最后的状态就是到起点S
的最短路径。弹出
U
之后,在后面的计算中,U
可能会再次更新状态(后来发现,U
借道别的结点去S
,路更近)。所以,U
可能需要重新入队列。 有可能只有很少结点重新进入队列,也有可能很多。这取决于图的特征。所以,SPFA 是不稳定的,所以根据题目的类型,我们要选择合适的算法。
SPFA 模板如下:
#include<bits/stdc++.h> using namespace std; const long long INF = 0x3f3f3f3f3f3f3f3f; const int N = 5e3+10; struct edge{ int to; long long w; edge(int tt,long long ww) {to = tt; w = ww;} }; long long dist[N]; int inq[N]; vector<edge> e[N]; void spfa(int s){ memset(dist,0x3f,sizeof(dist)); dist[s] = 0; //起点到自己的距离是0 queue<int> q; q.push(s); //从s开始,s进队列 inq[s] = 1; //起点在队列中 while(!q.empty()) { int u = q.front(); q.pop(); inq[u] = 0; //u已经不在队列中 if(dist[u] == INF) continue; for(int i = 0;i < e[u].size();i++) { //遍历u的邻居 int v = e[u][i].to; long long w = e[u][i].w; if(dist[v] > dist[u]+w) { //u的第i个邻居v,它借道u,到s更近 dist[v] = dist[u]+w; //更新邻居v到s的距离 if(!inq[v]) { //邻居v更新状态了,但v不在队列中,放进队列 q.push(v); inq[v] = 1; } } } } } int main(){ int n,m,s;cin>>n>>m>>s; for(int i = 1;i <= m;i++) { int u,v; long long w; cin>>u>>v>>w; e[u].push_back(edge(v,w)); } spfa(s); for(int i = 1;i <= n;i++) { if(dist[i]==INF) cout << -1; else cout << dist[i]; if(i != n) cout << " "; else cout << endl; } return 0; }