【学习笔记】差分约束
参考资料:
- 《算法导论》 - 差分约束和最短路径
- 《算法竞赛进阶指南》 - 差分约束系统
- Pecco - 算法学习笔记(11): 差分约束
- OI Wiki - 差分约束
- 佚名 - 差分约束系统
- 杂鱼 - [转载][研究总结]最短路、最长路与差分约束的最大解、最小解
在此表示感谢。
零、前置知识
- Bellman-Ford(SPFA) 求最短 / 长路、判断负环
一、什么是差分约束系统
差分约束系统 是一组包含 \(m\) 个不等式,有 \(n\) 个未知数的形如:
的不等式组(\(y_1, y_2, \cdots, y_m\) 已知)。
在算法竞赛中,很多题目会给出(或隐性地给出)一系列的不等关系,我们可以尝试把它们转化为差分约束系统来解决。
二、一些性质
明显地,若 \(\{x_1, x_2, \cdots, x_n\}\) 是差分约束系统的一组解,则 \(\{x_1 + d, x_2 + d, \cdots, x_n + d\}\) 也是这个差分约束系统的一组解。因为做差 \(d\) 会被消掉。
三、差分约束算法
1. 转化
我们将不等式组移项:
就会发现每一个不等式与求解最短路问题中的 三角形不等式 \(dis[v] \le dis[u] + w_{u,v}\) 相似,于是我们可以把这个差分约束系统转换成一个图论中的求最短路问题。
我们把每一个不等式看作一个最短路问题的三角形不等式建边,即从 \(x_{c^{'}_i}\) 向 \(x_{c_y}\) 连一条长度为 \(y_i\) 的 有向边。
2. 超级源点
既然是最短路问题,那么就会有一个源点。现在请问:源点是哪一个?
我们会发现,其实源点是哪一个其实并没有关系。但是我们有可能得到一个非连通图。
比如我们根据 洛谷 - P5960 【模板】差分约束算法 的样例构造图:

若我们选取节点 \(1\) 或 \(2\) 跑最短路,则会发现无法到达节点 \(3\)。
由此可见我们需要一个找到一个可以到达所有节点的点作为源点。
但是不好找,所以我们人为地构造一个源点 \(S\),我们称之为 超级源点。
我们从 \(S\) 向每一个节点连一条长度为 \(0\) 的有向边。
这样相当于在不等式组当中添加:
意思就是说使得解集中的任何一个值小于等于 \(0\)。
这样真的有解吗?
我们可以从上面解集的性质中得到,若 \(\{x_1, x_2, \cdots, x_n\}\) 是差分约束系统的一组解,我们只要找到一个 \(d\),使得所有的数加上 \(d\) 小于等于 \(0\)。因为 \(\{x_1 + d, x_2 + d, \cdots, x_n + d\}\) 依然是差分约束系统的一组解,同时也满足了我们在不等式组当中添加那 \(n\) 条与原点 \(S\) 的不等式,所以有解。
所以我们可以这样人为地添加一个超级源点。
同理,我们从 \(S\) 向每一个节点连一条长度为 \(w\) 的有向边也是可以的,证明就是把上面的 \(0\) 换成 \(w\)。
3. 超级源点的选定
可以选择任意一个不是图上节点的点,一般来说我们选择节点 \(0\) 或者节点 \(n + 1\) 为超级源点。
4. 求解
直接从源点 \(S\) 出发求解单源最短路,解就是从 \(S\) 出发到任意一点的最短路。
这样求出来的是该差分约束系统中的其中一组解。
5. 负环无解
不等式组一定有解吗?很明显不一定。
比如:
我们尝试求解一下。
我们发现:\(x_{1} \leq x_{2} - 1 \leq x_{3} - 2 \leq x_{1} - 3\),即 \(x_{1} \leq x_{1} - 3\)。
\(x_{1} \leq x_{1} - 3\),然而 \(x_{1} > x_{1} - 3\)。
发现矛盾了,所以该不等式组无解。
我们化成图:

发现有负环,不存在最短路,所以不等式也没有解。
6. 代码
/*Copyright (C) 2013-2022 LZE*/
#include<bits/stdc++.h>
#define fo(x) freopen(#x".in", "r", stdin); freopen(#x".out", "w", stdout);
#define INF 0x7fffffff
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N = 1000010;
const int M = 100010;
ll n, m;
struct GraphEdge {
ll to, next, w;
GraphEdge() {}
} edge[M];
ll head[N] = {0};
ll num = 0;
void add(ll u, ll v, ll w) {
edge[++num].to = v;
edge[num].w = w;
edge[num].next = head[u];
head[u] = num;
}
ll cnt[N] = {0};
ll dis[N] = {0};
bool vis[N] = {0};
queue<ll> q;
bool spfa(const ll &x) {
dis[x] = 0; q.push(x); vis[x] = true;
ll p, to, w;
while(!q.empty()) {
p = q.front(); q.pop();
vis[p] = false;
for(ll i = head[p]; i; i = edge[i].next) {
to = edge[i].to; w = edge[i].w;
if(dis[to] > dis[p] + w) {
dis[to] = dis[p] + w;
if(!vis[to]) {
vis[to] = true;
q.push(to);
if(++cnt[to] >= n + 1) {
return false;
}
}
}
}
}
return true;
}
int main() {
scanf("%lld%lld", &n, &m);
ll u, v, w;
for(ll i = 1; i <= m; i++) {
scanf("%lld%lld%lld", &u, &v, &w);
add(v, u, w);
}
for(ll i = 1; i <= n; i++) add(N - 1, i, 0);
for(ll i = 1; i <= n; i++) dis[i] = INF;
if(!spfa(N - 1)) {
printf("NO\n");
return 0;
}
for(ll i = 1; i <= n; i++) printf("%lld ", dis[i]);
printf("\n");
return 0;
}
7. 时间复杂度
由于使用 Bellman-Ford,所以时间复杂度为 \(O(mn)\)。
四、差分约束系统的最大解和最小解
1. 性质
在上文中,我们证明了从超级源点 \(S\) 向每一个节点连一条长度为 \(w\) 的有向边是可以的。
对于这样的差分约束系统,有一个性质:这样用最短路求出的解为该差分约束系统 \(x_1, x_2, \cdots, x_n\le w\) 的 最大解。
这里我粗略地证明一下。这个证明过程要结合 Bellman-Ford 算法的过程来说明。
2. 证明
设合法的最大解为 \(\{M_1, M_2, \cdots, M_n\}\),求出的最短路为 \(\{D_1, D_2, \cdots, D_n\}\)。
根据 Bellman-Ford 的过程,我们会将 \(\{D_1, D_2, \cdots, D_n\}\) 都赋值为 \(\infty\),然后检查所有的边对应的三角形不等式。一但发现有不满足三角形不等式的情况,则更新对应的 \(D\) 值。最后求出来的 \(\{D_1, D_2, \cdots, D_n\}\) 就是源点到每个点的最短路径长度。
如果一开始初始化 \(\{D_1, D_2, \cdots, D_n\}\) 的值分别为 \(\{M_1, M_2, \cdots, M_n\}\),则由于它们全都满足三角形不等式,则 Bellman-Ford 算法不会再更新任何 \(D\) 值,则最后得出的解就是 \(\{M_1, M_2, \cdots, M_n\}\)。
所以,初始值为无穷大时,算出来的是 \(\{D_1, D_2, \cdots, D_n\}\);初始值比较小的时候算出来的则是 \(\{M_1, M_2, \cdots, M_n\}\)。所以 \(\{D_1, D_2, \cdots, D_n\}\) 就是 \(\{M_1, M_2, \cdots, M_n\}\)。
3. 最小解
那么如何求满足 \(x_1, x_2, \cdots, x_n\ge w\) 的 最小解 呢?只需要求 最长路 就行了。
五、不等号
有一些题目不仅会给你 \(\le\) 号,还会有其他的不等号。
其实处理这些符号也很简单。
- \(x - y \le z\):\(x - y \le z\)
- \(x - y \ge z\):\(y - x \le -z\)
- \(x - y = z\):\(x - y \le z \land y - x \le -z\)
- \(x - y < z\):\(x - y \le z - \Delta\)
- \(x - y > z\):\(y - x \le - z - \Delta\)
其中 \(\Delta\) 是一个极小值,若 \(x, y\) 为整数,\(\Delta\) 可看作 \(1\)。
六、练习题
五倍经验:UVA - 1723 Intervals、SPOJ - 116 INTERVAL - Intervals、POJ - 1201 Intervals、AcWing - 362. 区间、FYMS-OI - #26. 「模板」差分约束系统(POJ 1201)

浙公网安备 33010602011771号