题解 P5960 【【模板】差分约束算法】
StudyingFather · · 题解
UPD(2020/12/23):修正了入队次数为
UPD(2024/08/22):修正了 dis
数组没有开 long long
导致溢出的问题。
一个差分约束系统是这样的:
给出
怎样解这个差分约束系统呢?我们将上面的不等式变形一下:
容易发现这个形式和最短路中的三角形不等式
因此我们就将这个问题转化为一个求最短路的问题:比如对于上面这个不等式,我们从
接下来,我们再新建一个
这个操作相当于新增了一个变量
然后以
负环的判断
建议先做 P3385,不过这里还是先大概讲一下吧。
首先,如果点
原图中有
用 Bellman-Ford 的话,最多会执行
啥你用的是 SPFA?
(偷偷告诉你 SPFA 码量比 Bellman-Ford 长而且最坏复杂度和 Bellman-Ford 一样)
那就统计一个点入队的次数,如果一个点进队了
扩展
很多时候差分约束的条件并不是简单的小于等于号,这时候我们需要稍微做点变形。
如果有
如果有
Code
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
using i64 = long long;
const int maxn = 5'000;
const int maxm = 10'000;
struct edge {
int v, w, next;
} e[maxm + 5];
int head[maxn + 5], tot[maxn + 5], vis[maxn + 5], cnt, n, m;
i64 dis[maxn + 5];
void addedge(int u, int v, int w) {
e[++cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
bool spfa(int s) {
queue<int> q;
memset(dis, 63, sizeof(dis));
dis[s] = 0, vis[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = 0;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if (dis[v] > dis[u] + e[i].w) {
dis[v] = dis[u] + e[i].w;
if (!vis[v]) {
vis[v] = 1, tot[v]++;
if (tot[v] == n + 1) return false; // 注意添加了一个超级源点
q.push(v);
}
}
}
}
return true;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) addedge(0, i, 0);
for (int i = 1; i <= m; i++) {
int v, u, w;
cin >> v >> u >> w;
addedge(u, v, w);
}
if (!spfa(0))
cout << "NO" << endl;
else
for (int i = 1; i <= n; i++) cout << dis[i] << ' ';
return 0;
}