分析:
spfa算法中,采用邻接表的方式,只用到有效的点(更新了临边的点),直到每个点都是最短距离为止。采用队列优化的方式存储每次更新了的点,每条边最多遍历一次。如果存在负权回路,从起点1出发,回到1距离会变小, 会一直在三个点中循环。
$\cal{P.S.}$防止有多个环,因此把每个点都放到序列里面。
for(int i = 1;i <= n; ++i)
{
st[i] = true;
q.push(i);
}
spfa判断负环
$\cal{Question:}$
1.spfa 的st 和 dijkstra 的st 有什么区别?
迪杰斯特拉算法的st数组表示距离初始点最小的点集合,spfa的st数组表示判断是否在队列里面
2.dist不初始化后,还能更新dist数组吗?
不管dist数组的初值是多少,都是可以的。因为只要有负环存在,就必然有某些点的距离是负无穷,所以不管距离被初始化成何值,都一定严格大于负无穷,所以一定会被无限更新(太精妙了)。
3.为什么存在环就一定是负环?
是因为有一个节点重复更新了两次, 而只有负环才能让一个节点重复更新两次
4.如果从队列里取节点就判断st可以吗?
spfa的st数组表示判断是否在队列里面, 当每次要往队列里加的时候才判断。
5.spfa是怎么从bellmanford算法演变的?
贝尔曼算法每次迭代的时候dist[b] = dist[a] + w; 不一定会执行,于是队列存放变小的节点(a节点)
,只要队列不空(还有变小的节点),那么更新该节点的所有出边。
样例
3 3
1 2 -1
2 3 4
3 1 -4
算法1
(spfa) $O(n+m)$
时间复杂度
参考文献
C++ 代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 2010, M = 10010;
int ne[M],e[M], w[M], h[N], idx;
int dist[N], cnt[N], n;
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b; ne[idx] = h[a]; w[idx] = c; h[a] = idx++;
}
int spfa()
{
queue<int> q;
for(int i = 1;i <= n; ++i)
{
st[i] = true;
q.push(i);
}
while(q.size())
{
int t = q.front();
q.pop();
st[t] = false;
for(int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if(dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
cnt[j] = cnt[t] + 1;
if(!st[j])
{
q.push(j);
st[j] = true;
}
if(cnt[j] >= n)return true;
}
}
}
return false;
}
int main()
{
memset(h, -1, sizeof h);
int a, b ,c;
int m; cin >> n >> m;
while(m--)
{
cin >> a >> b >> c;
add(a, b, c);
}
if(spfa())puts("Yes");
else puts("No");
return 0;
}