目录
- 1.先决条件
- 2.SPFA算法
- 2.1适用范围
- 2.2算法流程
- 2.3判断负环
- 3.相关练习
1.先决条件
阅读本文前,需阅读:
- 最短路算法之Dijkstra(一).
2.SPFA算法
SPFA 在无负边权图大概率会被构造数据弄的AC 不了,所以尽量不使用SPFA .
关于SPFA,它死了.
2.1适用范围
SPFA适用于任何带权无向或有向图,与Dijsktra不同的是,SPFA适用于负边权图.
但要注意判断负环,负环情况下是求不出最短路径或权值的.
2.2算法流程
d[i] 表示点i 到起点s 的最短距离,初始化d = {INF} ,d[s] = 0 .
vis[i] = true 表示点i 在队列q 中,初始化vis = {false} .
将点s 加入队列q ,赋值vis[s] = true ,进入循环,直到队列q为空终止:
每次循环,取出队头元素点u ,赋值vis[u] = false .
遍历点u 的所有出边{u,v,w} ,若d[v] > d[u] + w ,则更新d[v] = d[u] + w .
若点v 不在队列q 中,则将点v 入队q ,vis[v] = true .
该算法时间复杂度一般情况下较优,但在CCF精心构造的数据下,很可能TLE .
在2.1适用范围中提到:“负环情况下是求不出最短路径或权值的.”,我会在2.3判断负环中阐述判断负环的办法.
code:(未判断负环,有向图)
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
const int INF = 0x3f3f3f;
vector<pair<int, int> > g[N];
int d[N];
bool vis[N];
int main()
{
int n, m, s;
scanf("%d %d %d", &n, &m, &s);
for(int i = 0; i < m; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
g[u].push_back({v, w});
}
for(int i = 1; i <= n; i++) {
vis[i] = false;
d[i] = INF;
}
d[s] = 0;
queue<int> q;
q.push(s);
while(q.size()) {
int u = q.front();
q.pop();
vis[u] = false;
for(auto i : g[u]) {
int v = i.first, w = i.second;
if(d[v] > d[u] + w){
d[v] = d[u] + w;
if(!vis[v]){
vis[v] = true;
q.push(v);
}
}
}
}
for(int i=1;i<=n;i++) printf("%d ", d[i]);
return 0;
}
2.3判断负环
我们知道,只要一个图是连通图,从一个点最多经过N 个点就能到达任意的另一个点.
观察发现:
如果图中不存在负环,一条路径最多经过N 个点;
如果图中存在负环,则SPFA算法会一直绕着负环走.
所以,一条路径经过的节点数大于N 说明图中存在负环.
code:
//SPFA判负环版
//JiansYuan 2020.8.4
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
const int INF = 0x3f3f3f;
vector<pair<int, int> > g[N];
int d[N], cnt[N]; //cnt记录经过的节点数
bool vis[N];
int main()
{
int n, m, s;
scanf("%d %d %d", &n, &m, &s);
for(int i = 0; i < m; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
g[u].push_back({v, w});
}
for(int i = 1; i <= n; i++) {
vis[i] = false;
d[i] = INF;
cnt[i] = 0;
}
d[s] = 0;
queue<int> q;
q.push(s);
bool flag = false;//用于记录图中是否存在负环
while(q.size()) {
int u = q.front();
q.pop();
vis[u] = false;
for(auto i : g[u]) {
int v = i.first, w = i.second;
if(d[v] > d[u] + w){
d[v] = d[u] + w;
cnt[v]++;
if(cnt[v] > n) flag = true;
if(!vis[v]){
vis[v] = true;
q.push(v);
}
}
}
}
if(flag) printf("ERROR!\n");
else for(int i=1;i<=n;i++) printf("%d ", d[i]);
return 0;
}
3.相关练习
由于大部分题目都会卡SPFA ,所以题目比较难找QWQ .
洛谷P3371
JiansYuan
2020.8.4