差分约束系统
一、定义
如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
观察xj-xi<=bk,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v] [1] 。因此,以每个变量xi为结点,对于约束条件xj-xi<=bk,连接一条边(i,j),边权为bk。我们再增加一个源点s,s与所有定点相连,边权均为0。对这个图,以s为源点运行Bellman-ford算法(或SPFA算法),最终{d[ i]}即为一组可行解。
二、实现
求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。
并且,在实际应用中,多使用SPFA算法实现。
那么如果在一个未知数定死的情况下,要求其它所有未知数的最小值怎么办?只要反过来求最长路径就可以了。最长路径中的三角不等式与最短路径中相反:
d(v) >= d(u) + w(u, v)
也就是 d(v) - d(u) >= w(u, v)
所以建图的时候要先把所有不等式化成大于等于号的。其它各种过程,包括证明为什么解出的是最小值的证法,都完全类似。
三、应用
总之对于不同的题目,给出的条件都不一样,我们首先需要关注问题是什么,
①求 xn-x1 的最大值:
将所有不等式化为 a-b<=w 的形式,对每个不等式建边:b 到 a 权值为 w
②求 xn-x1 的最小值
将所有不等式化为 a-b>=w 的形式,对每个不等式建边:b 到 a 权值为-w
四、不等式组的转化
做题时可能会遇到不等式中的符号不相同的情况,但我们可以对它们进行适当的转化
(1)方程给出:X[n-1]-X[0]>=T ,可以进行移项转化为: X[0]-X[n-1]<=-T。
(2)方程给出:X[n-1]-X[0]<T, 可以转化为X[n-1]-X[0]<=T-1。
(3)方程给出:X[n-1]-X[0]=T,可以转化为X[n-1]-X[0]<=T&&X[n-1]-X[0]>=T,再利用(1)进行转化即可
例题:
题目描述
Bessie 在过去的 M 天内参加了 N 次挤奶。但她已经忘了她每次挤奶是在哪个时候了。
对于第 i 次挤奶,Bessie 记得它不早于第 Si 天进行。另外,她还有 C条记忆,每条记忆形如一个三元组 (a,b,x),含义是第 b 次挤奶在第a 次挤奶结束至少 x天后进行。
现在请你帮 Bessie 算出在满足所有条件的前提下,每次挤奶的最早日期。
保证 Bessie 的记忆没有错误,这意味着一定存在一种合法的方案,使得:
第 i 次挤奶不早于第 Si天进行,且不晚于第 M 天进行;所有的记忆都得到满足;
输入格式
第一行三个整数 N,M,C。保证 1≤N,C≤105,92≤M≤109。
接下来一行包含 N 个整数 S1,S2,…,Sn,
下面 C行每行三个整数 a,b,x,描述一条记忆。
输出格式
输出 N行,每行一个整数,第 i 行的数表示第 i 次挤奶的最早日期。
输入输出样例
输入
4 10 3
1 2 3 4
1 2 5
2 4 2
3 4 4
输出
1
6
3
8
思路
题解:
#include <bits/stdc++.h>
using namespace std;
struct node {
int val, next, len;
} e[1000086];
int n, m, c;
int cnt;
int head[1000086];
int dist[1000086];
int sum[1000086];
int vis[1000086];
int S[1000086];
const int inf = 0x3f3f3f3f;
void AddEdge (int u, int v, int w) {
e[++cnt].val = v;
e[cnt].next = head[u];
e[cnt].len = w;
head[u] = cnt;
}
bool SPFA () {
queue <int> q;
int s = 0;
for (int i = 1; i <= n; i++)
dist[i] = -inf;
dist[s] = 0;
sum[s] = 1;
vis[s]++;
q.push(s);
while (!q.empty()) {
int cur = q.front();
q.pop();
sum[cur] = 0;
for (int p = head[cur]; p > 0; p = e[p].next)
if (dist[e[p].val] < dist[cur] + e[p].len) {
dist[e[p].val] = dist[cur] + e[p].len;
vis[e[p].val]++;
if (vis[e[p].val] >= n + 1)
return true;
if (!sum[e[p].val]) {
q.push(e[p].val);
sum[e[p].val] = 1;
}
}
}
return false;
}
int main () {
scanf("%d%d%d", &n, &m, &c);
for (int i = 1; i <= n; i++) {
scanf("%d", &S[i]);
AddEdge(0, i, S[i]);
}
for (int i = 1, u, v, w; i <= c; i++) {
scanf("%d%d%d", &u, &v, &w);
AddEdge(u, v, w);
}
bool hahaha = SPFA();
for (int i = 1; i <= n; i++)
printf("%d\n", dist[i]);
return 0;
}