差分约束.
差分约束
差分约束系统是一种特殊的 nnn 元一次不等式组,它包含 nnn 个变量 x1,x2,⋯ ,xnx_1,x_2,\cdots,x_nx1,x2,⋯,xn 以及 mmm 个约束条件,每个约束条件是由其中的 两个变量做差 构成的,形如 xi−xj≤ckx_i-x_j\le c_kxi−xj≤ck,其中 ckc_kck 是常数且 i≠ji\neq ji=j。
我们要解决的问题就是:求一组解 x1=a1,x2=a2,⋯ ,xn=anx_1=a_1,x_2=a_2,\cdots,x_n=a_nx1=a1,x2=a2,⋯,xn=an,使得所有约束条件得到满足,或者判断出无解。
图论建模
差分约束系统中的每个约束条件 xi−xj≤ckx_i-x_j\le c_kxi−xj≤ck 都可以变成 xi≤xj+ckx_i\le x_j+c_kxi≤xj+ck 的形式,这与单源最短路中的 三角不等式 dist[v]≤dist[u]+wdist[v]\le dist[u]+wdist[v]≤dist[u]+w 非常相似。
所以我们可以这样解释这个约束条件:源点到 iii 的最短距离,必然小于等于 源点到 jjj 的距离 加上 (i,j)(i,j)(i,j) 之间的 边权 ckc_kck(uuu 到 vvv 是 单向边,边权为 www)。
显然,这样的条件在一个 具有最短路性质的图中 是必然成立的。
那么源点的选取可以简单一些,如我们令 000 为 超级源点,且向所有结点连接一条边权为 000 的单向边。
此时整个图连通,而具有最短路性质的图只需要判断图中是否有负环即可。
又因为 dijkstradijkstradijkstra 只能用于处理非负权边的图,所以我们考虑用 Bellman−FordBellman-FordBellman−Ford 算法。
简单介绍一下 Bellman−FordBellman-FordBellman−Ford 算法。
首先令源点 sss 的最短路 dist[s]=0dist[s]=0dist[s]=0,其他的都是无穷大。
然后遍历所有的边 (u,v)(u,v)(u,v),如果 dist[u]dist[u]dist[u] 不是无穷大,并且满足 dist[v]>dist[u]+wdist[v]>dist[u]+wdist[v]>dist[u]+w,那么就代表能松弛。
我们遍历所有的边 n−1n-1n−1 轮,因为 边的遍历顺序问题,所有的点最多需要 n−1n-1n−1 轮才能松弛完。
边的遍历顺序问题指的是,整个图是一条链,且与源点有关的边被放在最后一条,以此类推。
所以一轮遍历只松弛了一个点,至多需要 n−1n-1n−1 轮遍历 才能松弛所有点。
检查负环 的方法就是,当遍历了 n−1n-1n−1 轮后,再遍历一轮,如果还能进行松弛,说明必然有负环。
#include <bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define int long long
#define endl '\n'
#define PII pair<int,int>
#define INF 1e18
const int N = 1e4;int dist[N], vis[N];
vector<PII> g[N];bool BellmanFord(int s, int n) {dist[s] = 0;bool flag = 0;for (int i = 1; i <= n; i++) {flag = 0;for (int u = 1; u <= n; u++) {for (auto [v, w] : g[u]) {if (dist[u] != INF && dist[v] > dist[u] + w) {dist[v] = dist[u] + w;flag = 1;}}}if (!flag) break;}return flag; // 如果 flag = 1,说明有负环,否则没有负环
}void slove () {int n, m;cin >> n >> m;while (m--) {int op, u, v, w;cin >> op;if (op == 1) {cin >> u >> v >> w;// dist[u] >= dist[v] + w// dist[v] <= dist[u] - wg[u].emplace_back(v, -w);} else if (op == 2) {cin >> u >> v >> w;// dist[u] - dist[v] <= w// dist[u] <= dist[v] + wg[v].emplace_back(u, w);} else {cin >> u >> v;g[u].emplace_back(v, 0);g[v].emplace_back(u, 0);}}for (int i = 1; i <= n; i++) {g[0].emplace_back(i, 0);}bool is_ok = BellmanFord(0, n);if (is_ok) cout << "No" << endl;else cout << "Yes" << endl;
}signed main () {ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);slove();
}