差分约束学习笔记

一.差分约束系统

如果一个系统 n n n 个变量 x 1 , x 2 , ⋅ ⋅ ⋅ , x n x_1,x_2,···,x_n x1,x2,⋅⋅⋅,xn m m m 个约束条件(也是不等式)和** m m m 个常量 w 1 , w 2 , ⋅ ⋅ ⋅ , w m w_1,w_2,···,w_m w1,w2,⋅⋅⋅,wm。每一个不等式形如以下格式 x i − x j ≤ w k x_i - x_j \le w_k xixjwk 1 ≤ i , j ≤ n 1 \le i,j \le n 1i,jn 1 ≤ k ≤ m 1 \le k \le m 1km)。则称之为差分约束系统。**

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

有如下几个问题:

1.有没有解

2.求一组解

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

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

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

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

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

二.解的判断与求解

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

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


我们将这个格式做一个变换:既然 x i − x j ≤ w k x_i - x_j \le w_k xixjwk,则可以转换为 x i ≤ x j + w k x_i \le x_j + w_k xixj+wk

如果这时把 x x x 看成就是最短路中的 d i s dis dis 数组,则 d i s i ≤ d i s j + w k dis_i \le dis_j + w_k disidisj+wk

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

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 x_i xi 一定满足 ≤ x j + w k \le x_j + w_k xj+wk,恰好满足不等式!

如果将 w k w_k wk 看成 j → i j \to i ji 的一条边,则恰好就可以完美吻合!


所以我们尝试将每一个不等式组 x i − x j ≤ w k x_i - x_j \le w_k xixjwk 变成 j → i j \to i ji 的一条有向边,**边权为 w k w_k wk。**也就是将每一个约束条件都抽象成了一条边。

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

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

注意,这时候的图包含负权边(谁说 w k w_k wk 不能是负数了?),不能使用 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 ≤ − 4 x_1 - x_2 \le -4 x1x24,两边同时取相反数得到: x 2 − x 1 ≥ 4 x_2 - x_1 \ge 4 x2x14,但是这样又和第三个式子矛盾了,所以这个差分约束系统无解。

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

三.特殊处理

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

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

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

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

这时候我们分情况。

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

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

但是这个时候任意取两个数 x i − x j x_i - x_j xixj 都是 0 0 0,而 0 0 0 显然 ≤ \le 全体非负数,一定成立。

如果有 w w w 是负数

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

因为有一个很巧合的点: 0 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 n n 的变量,这些式子形如:

第一种格式, x a − x b ≥ c x_a - x_b \ge c xaxbc
第二种格式, x a − x b ≤ c x_a - x_b \le c xaxbc
第三种格式, x a = x b x_a = x_b xa=xb

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

x a − x b ≥ c x_a - x_b \ge c xaxbc 可以变成 x b − x a ≤ − c x_b - x_a \le -c xbxac,符合基本格式。
x a − x b ≤ c x_a - x_b \le c xaxbc 本来就可以使用差分约束求解。
x a = x b x_a = x_b xa=xb 可以变成 x a − x b ≤ 0 , x b − x a ≤ 0 x_a - x_b \le 0,x_b - x_a \le 0 xaxb0,xbxa0,也符合基本格式。

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

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 a-b ab 的最大或最小值。

最大值:以 b b b 为起点跑最短路, d i s a dis_a disa 就是答案。

最小值:以 b b b 为起点跑最长路, d i s a dis_a disa 就是答案。

五.第四个问题

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

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

最大值:超级源点跑最短路,把所有的 d i s i dis_i disi 加在一起就是答案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值