题解
学习博客↓
题解 P5960 【【模板】差分约束算法】
记几个重点:
差分约束可以用最短路做,跑一遍最短路,此时最短路的答案 d i d_i di,也正是原不等式组的一个解 x i x_i xi
差分约束问题可以转化为最短路或最长路问题
连边后求最短路 :
将
x
j
−
x
i
≤
k
x_j-x_i\leq k
xj−xi≤k 变形为
x
j
≤
x
i
+
k
x_j \leq x_i+k
xj≤xi+k ,即从
i
i
i 到
j
j
j 连一条边权为
k
k
k 的边,加入超级源点后求最短路,得到
x
i
≤
0
x_i\leq 0
xi≤0 所有
x
x
x 最大解 即所有解都是小于等于0的
连边后求最长路 :
将
x
j
−
x
i
≤
k
x_j-x_i\leq k
xj−xi≤k 变形为
x
i
≥
x
j
−
k
x_i \ge x_j-k
xi≥xj−k ,即从
j
j
j 到
i
i
i 连一条边权为
−
k
-k
−k 的边,加入超级源点后求最长路,得到
x
i
≤
0
x_i\leq 0
xi≤0 所有
x
x
x 最小解 即所有解都是大于等于0的
当图中出现正环,求最长路时,方程组无解
当图中出现负环,求最短路时,方程组也无解
这里求最短路最长路都可以
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
int n, m, K;
namespace SPFA { //spfa 板子
struct edge {
int to, next;
int w;
} e[N];
int head[N], tot;
void add(int u, int v, int w) {
e[++tot] = {v, head[u], w};
head[u] = tot;
}
/* spfa实现方法:
1.开一个队列 先将开始的节点放入
2.每次从队列中取出一个节点u
遍历与u相通的v节点 查询比对dis[v] 和 dis[u]+w[u,v]
如果dis[v]>dis[u]+w 说明需要更新操作
1.存入最短路
2.由于改变了原有的长度 所以需要往后更新 与这个节点相连的最短路
(即:判断下是否在队列 在就不用重复 不在就加入队列 等待更新)
3.在这期间可以记录这个节点的进队次数,判断是否存在负环
3.直到队空
* */
int dis[N];//最短路
bool vis[N]; //判断是否在队列里
int in[N]; //入队次数
queue<int> q;
bool spfa(int st) {
//init
memset(vis, false, sizeof(vis));
vis[st] = true;
memset(dis, -1, sizeof(dis));//最长路初始化
dis[st] = 0;
memset(in, 0, sizeof(in));
in[st] = 1;
q.push(st);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
int w = e[i].w;
if (dis[v] < dis[u] + w) { //这里是求最长路 //求最短路 dis[v]>dis[u]+w
dis[v] = dis[u] + w;
if (!vis[v]) {
q.push(v);
vis[v] = true;
++in[v];
if (in[v] > n + 1) { //判断环 因为加了一个超级源点 故应跟 n + 1 而不是 n 比较
return false;
}
}
}
}
}
return true;
}
}
using namespace SPFA;
int main() {
ios::sync_with_stdio(0);
cin >> n >> m; //n个自变量 m个不等式
for (int i = 1, u, v, w; i <= m; ++i) {
cin >> u >> v >> w;
add(u, v, -w);
}
for (int i = 1; i <= n; ++i) {
add(0, i, 0); // 加入一个超级源点
}
if (!spfa(0)) {
cout << "NO" << endl;
} else {
for (int i = 1; i <= n; ++i) {
cout << dis[i] << ' ';
}
}
return 0;
}