差分约束学习笔记

一.差分约束系统

如果一个系统\(n\) 个变量 \(x_1,x_2,···,x_n\)\(m\) 个约束条件(也是不等式)和\(m\) 个常量 \(w_1,w_2,···,w_m\)。每一个不等式形如以下格式 \(x_i - x_j \le w_k\)\(1 \le i,j \le n\)\(1 \le k \le m\))。则称之为差分约束系统。

这个名字的由来是这样的:“差分”代表的是每一个约束条件里面都是做差的形式,“约束”代表的是每一个差都有一个最大值上界作为约束,“系统”字面意思。

有如下几个问题:

1.有没有解

2.求一组解

3.一组变量(\(a - b\))的极值

4.在额外条件下,变量和最值

...

容易发现,差分约束系统的解要么就无解,要么就有无数组解。

且这东西很容易识别,如果有一堆不等式每一个都形如 \(x_i - x_j \le w_k\) 这个格式,那么这个解决方法十有八九就是差分约束了。

这个东西的问题很清晰,接下来我们来看如何解决提出的这几个问题。

二.解的判断与求解

差分约束做了一个模型转化,将一个不等式组的问题转化为了一个图论问题。是不是很神奇?

在大学的计算机课程中,有一个东西叫做问题规约,即将一个问题的模型奇妙地转化为了另一个问题的模型。现在我告诉你,你只需要会求解图论中的最短路,你就可以做这个问题了。


我们将这个格式做一个变换:既然 \(x_i - x_j \le w_k\),则可以转换为 \(x_i \le x_j + w_k\)

如果这时把 \(x\) 看成就是最短路中的 \(dis\) 数组,则 \(dis_i \le dis_j + w_k\)

咦,这不就很像最短路中的松弛操作吗?

if (dis[v] > dis[u] + w)
	dis[v] = dis[u] + w;

上面的两行代码相当于 dis[v] = min(dis[v], dis[u] + w) 这条语句。

于是我们得知,当进行一次松弛操作的时候,div[v] 一定 \(\le\) dis[u] + w,即 \(x_i\) 一定满足 \(\le x_j + w_k\),恰好满足不等式!

如果将 \(w_k\) 看成 \(j \to i\) 的一条边,则恰好就可以完美吻合!


所以我们尝试将每一个不等式组 \(x_i - x_j \le w_k\) 变成 \(j \to i\) 的一条有向边边权为 \(w_k\)也就是将每一个约束条件都抽象成了一条边。

这时我们设任意的一个点为 \(S\),也就是起点,假设 \(x_S = 0\)。因为如果有解,就会有无数的解,就一定有 \(x_S = 0\) 为基础的这一组解。

则如果把该松弛的都松弛完了,也就是跑完了全图的从 \(S\) 开始的单源最短路,则如果 \(\forall 1 \le i \le n,x_i = dis_i\),对于任何的不等式都一定满足。

注意,这时候的图包含负权边(谁说 \(w_k\) 不能是负数了?),不能使用 dijkstra,需要使用 spfa 算法。


那么如何判断有没有解呢?

这个问题很简单,在上文的字里行间中就存在答案,这个字眼就是“跑完了单源最短路”。如果有某一个点的最短路不存在,则就是无解的。

什么?最短路不存在?你可以想到,不存在最短路,也就是无解,当且仅当图包含负环。

恰好 spfa 又可以判负环,于是在 spfa 求解最短路的过程中顺便判一下负环即可。

举一个例子:

这样的图中显然存在负环,不妨将其重新抽象成差分约束系统:

\[ \begin{matrix} x_1 - x_3 \le -2 \\ x_3 - x_2 \le -2 \\ x_2 - x_1 \le 3 \\ \end{matrix} \]

不妨将第一个式子和第二个式子联立起来,得到:\(x_1 - x_2 \le -4\),两边同时取相反数得到:\(x_2 - x_1 \ge 4\),但是这样又和第三个式子矛盾了,所以这个差分约束系统无解。

这样我们就同时解决了第一个和第二个问题。

三.特殊处理

看上去我们解决了第二个问题,但实际上第二个问题还没有结束呢!

\(2^{+}\):如果没有唯一的起点也就是 \(S\),也就是建出的图压根就不弱连通,怎么办呢?

这时候,我们可以弄一个超级源点,向所有点连一条边权为 \(0\) 的边。

但这个时候就有人会问了:那么所有点的最短路值不就全是 \(0\) 了吗?

这时候我们分情况。

如果所有的约束条件的 \(w\) 都是非负数

这时候所有点的最短路的值的确有可能会全部都是 \(0\)

但是这个时候任意取两个数 \(x_i - x_j\) 都是 \(0\),而 \(0\) 显然 \(\le\) 全体非负数,一定成立。

如果有 \(w\) 是负数

这个时候最短路的值也有可能被不是 \(0\)

因为有一个很巧合的点:\(0\) 显然 \(\ge\) 全体负数,所以也一定成立。

四.模板

P5960 【模板】差分约束

#include <bits/stdc++.h>
using namespace std;
int n, m;
const int N = 5010;
vector<pair<int, int> > v[N];
int dis[N], cnt[N];
bool vis[N];

bool spfa(int s) {//spfa算法判断负环
	queue<int> q;
	for (int i = 1; i <= n; i++)
		dis[i] = 1e17;
	dis[s] = 0, vis[s] = 1;
	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for (auto [to, w] : v[u])
			if (dis[to] > dis[u] + w) {
				dis[to] = dis[u] + w;
				cnt[to] = cnt[u] + 1;
				if (cnt[to] >= n)
					return 0;//找到负环
				if (!vis[u])
					q.push(to), vis[to] = 1;
			}
	}
	return 1;
}

int main() {
	cin >> n >> m;//这里设n+1号点为超级源点
	n++;
	for (int i = 1; i < n; i++)
		v[n].push_back({i, 0});//连边
	for (int i = 1; i <= m; i++) {
		int x, y, w;
		cin >> x >> y >> w;
		v[y].push_back({x, w});//建图
	}
	if (!spfa(n))
		cout << "NO\n";//发现负环就无解了
	else {
		for (int i = 1; i < n; i++)//输出每一个点的最短路作为一组解
			cout << dis[i] << " ";
		cout << endl;
	}
	return 0;
}

P1993 小 K 的农场

题面:有一堆式子和 \(n\) 的变量,这些式子形如:

第一种格式,\(x_a - x_b \ge c\)
第二种格式,\(x_a - x_b \le c\)
第三种格式,\(x_a = x_b\)

这下虽然不是普通的差分约束系统,但我们还是可以将其进行一些变换:

\(x_a - x_b \ge c\) 可以变成 \(x_b - x_a \le -c\),符合基本格式。
\(x_a - x_b \le c\) 本来就可以使用差分约束求解。
\(x_a = x_b\) 可以变成 \(x_a - x_b \le 0,x_b - x_a \le 0\),也符合基本格式。

这样就可以变成正宗的差分约束系统了。

int main() {
	cin >> n >> m;
	n++;
	for (int i = 1; i < n; i++)
		v[n].push_back({i, 0});
	for (int i = 1; i <= m; i++) {
		int op;
		cin >> op;
		if (op == 1) {
			int x, y, w;
			cin >> x >> y >> w;
			v[x].push_back({y, -w});
		} else if (op == 2) {
			int x, y, w;
			cin >> x >> y >> w;
			v[y].push_back({x, w});
		} else {
			int x, y;
			cin >> x >> y;
			v[x].push_back({y, 0});
			v[y].push_back({x, 0});//建图
		}
	}
	if (!spfa(n))
		cout << "No\n";
	else
		cout << "Yes\n";
	return 0;
}

四.第三个问题

3.求 \(a-b\) 的最大或最小值。

最大值:以 \(b\) 为起点跑最短路,\(dis_a\) 就是答案。

最小值:以 \(b\) 为起点跑最长路,\(dis_a\) 就是答案。

五.第四个问题

4.额外条件下,变量和最大最小值

额外条件是因为差分约束可能有无穷组解,所以必须做出额外约束。

最大值:超级源点跑最短路,把所有的 \(dis_i\) 加在一起就是答案。

posted @ 2025-03-31 09:25  wusixuan  阅读(29)  评论(0)    收藏  举报