差分约束系统

update in : 2022.4.16

Part 1 定义

  差分约束系统是一种特殊的 \(N\) 元一次不等式组,它包含 \(N\) 个变量,\(X1~Xn\) 以及 \(m\) 个约束条件,每一个约束条件都是由两个变量作差构成的,形如 \(X_i-X_j≤C_k\) ,其中 \(C_k\) 是常数,\(1≤i,j≤N,1≤k≤M\),解决的问题是求一组解,\(X_1=a_1\)\(X_2=a_2···X_n=a_n\) ,使得所有的约束条件都得到满足。

  差分约束系统的每一个约束条件 \(X_i-X_j≤C_k\) 可以变形为最短路中的三角不等式,\(X_i≤X_j+C_k\) ,因此,可以把每一个变量 \(X_i\) 看作有向图中的一个结点 \(i\) ,对于每一个约束条件 \(X_i-X_j≤C_k\) ,为从结点 \(j\) 想结点 \(i\) 连一条长度为 \(C_k\) 的有向边

Part 2 基本方法

  一般来说,需要你求的解可能是最小解或是最大解,这时候就需要我们转化一下了ヽ(ー_ー)ノ。

  例如上边的式子,可转化为:\(x_i >= k + x_j\)的形式,可以发现,这种形式很像最短路中的更新方法:

if(dis[y] > dis[x] + w[x][y]) dis[y] = dis[x] + w[x][y];

  所以这种约束问题我们就可以用最短路的思想来解决ヾ(๑╹◡╹)ノ"

Part 3 例题

  通过几个例题来理解一下最短路求解不等式组的实现方式。

UVA1723 Intervals

  大体题意:给定 \(n\) 个区间,每个区间内至少选 \(c_i\) 个数。

  设 \(d[i]\) 表示从 \(0 ~ i\) 中选了几个数

  那么这个题就转化成了解 \(n\) 个不等式。

看样例:

3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

把它转化成方程组

d[7] - d[2] >= 3
d[10] - d[7] >= 3
d[8] - d[5] >= 1
d[3] - d[0] >= 1
d[11] - d[9] >= 1

  通过消元可以得到 \(d[11] - d[0]\) 的最小值为 \(6\)。从 0 到 3 跑一边 SPFA 求最长路,发现答案也是 &6&。

  这不是个巧合,因为求最长路时,SPFA的松弛操作后,\(dis[v]>=dis[u]+line[i].w\),与不等式\(X_i>=X_j+C_k\) 性质一样,所以 \(SPFA\) 可以求出最小值,又因为不等式同大取大,所以要求出最长路。
  本题就是从 \(0-50000\) 选出最少的数,使每个区间至少有 \(c\) 个数被选,这是求最小值,所以我们待会要将所有的不等式转换成">="的形式 我们用数组\(d[i]\) 表示 \(0-i\) 之间至少要选多少个数,所以对于每一个约束条件,可以看成 \(d[b_i]-d[a_i-1]>=c_id[bi​]−d[ai​−1]>=ci​\)

  \(d[b_i​]>=c_i​+d[a_i​−1]\)

  因此我们在 \(a[i-1]\)\(b[i]\) 之间建一条长度为 \(c[i]\) 的边

code:

/**
 *	author: zcxxxxx
 *	creater: 2022.3.30
**/
#include <bits/stdc++.h>
using namespace std;
const int A = 1e2 + 7;
const int B = 1e3 + 7;
const int C = 1e4 + 7;
const int D = 5e5 + 7;
const int E = 1e6 + 7;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
inline int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * f;
}
int n, cnt, mn = INF, mx = 0;
int dis[D], head[D];
bool vis[D];
struct edge {
	int nxt, to, dis;
}line[E << 1];
void add(int u, int v, int w) {
	line[++cnt].nxt = head[u];
	line[cnt].to = v;
	line[cnt].dis = w;
	head[u] = cnt;
}
void spfa() {
	queue<int> q;
	q.push(0);
	dis[0] = 0;
	vis[0] = 1;
	while(q.size()) {
		int x = q.front(); q.pop();
		vis[x] = 0;
		for(int i = head[x]; i; i = line[i].nxt) {
			int y = line[i].to, z = line[i].dis;
			if(dis[y] < dis[x] + z) {
				dis[y] = dis[x] + z;
				if(!vis[y]) {
					vis[y] = 1;
					q.push(y);
				}
			} 
		}
	}
}
int main() {
	int t = read();
	while(t --) {
		cnt = 0;
		memset(head, 0, sizeof head);
		memset(dis, -INF, sizeof dis);
		n = read();
		for(int u, v, w, i = 1; i <= n; ++ i) {
			u = read(), v = read(), w = read();
			add(u, v + 1, w);
			mx = max(mx, v + 1);
		}
		for(int i = 1; i <= mx; ++ i) {
			add(i - 1, i, 0);
			add(i, i - 1, -1);
		}
		spfa();
		cout << dis[mx] << "\n";
		if(t) puts("");
	}
	return 0;
}
posted @ 2022-01-28 22:03  zcxxxxx  阅读(50)  评论(0编辑  收藏  举报