>>>>本篇博客希望帮助你们更好的记忆模板,如果需要深刻理解还需日后的刷题巩固>>>>>
废话不多说我们先上图
[图片来源于]https://www.acwing.com/blog/content/140/
如侵必删
Bellman-ford(spfa的前置技能—目前对我来说很少用)
具体思路:
- 迭代n次
- 循环所有边 a b w 存在一条从a走向b的边 权重w
- dist[b] = min(dis[b],dist[a]+w) 松弛操作
- 从而 所有的边都 dist[b]<= dis[a]+w
SPFA(负权边常用 目前没被卡em)
求最短路:
思路:
对Bellman-ford做优化,
Bellman-ford每一次迭代都会对每条边进行更新,但是其实不是每一条边都需要更新
所以spfa 可以是对dist[b] =min(dis[b],dis[a]+w)
只有dis[a]变小了所以dis[b]才会变小,所以使用宽搜队列做优化
操作:
queue<- 1 while(queue)不空
{
1.取出对头并且删去对头
2.然后遍历t的所有出边并且更新 ///更新前需要判断是否已经加入了队列了
3.如果更新成功那么需要让该点入队列
}
代码:
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
queue<int> q;
存储待更新的点
q.push(1);
st[1] = true;
st存的是当前这个点是否在队列当中
存重复的点在队列当中是没有意义的
while (q.size())
{
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i])///更新t的所有领边
{
int j = e[i];
if (dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if (!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
判断负权回路:
抽屉原理;
dist[x] 当前1~x的最短距离
cnt[x] 当前这个最短路边的数量
每次更新的dist的时候
dist[x] = dist[t] + w[i]
cnt[x] = cnt[t] +1
如果x>=n也就是从1~x至少经过了n条边
所以至少经过了n+1个点
代码如下:
bool 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 (cnt[j] >= n) return true;
if (!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
return false;
}