差分约束系统是下面这种形式的多元一次不等式组(
(每个不等式称为一个约束条件,都是两个未知量之差小于或等于某个常数)
在算法竞赛中,很多题目会给出(或隐性地给出)一系列的不等关系,我们可以尝试把它们转化为差分约束系统来解决。
我们设
观察这个不等式与最短路问题中的三角形不等式
这样建出的有向图,它的每个顶点都对应差分约束系统中的一个未知量,源点到每个顶点的最短路对应这些未知量的值,而每条边对应一个约束条件。
那么问题来了,既然是最短路,源点在哪里呢?
实际上取哪个点为源点是无关紧要的,但是,有时候我们得到的图不是连通的,这样求出来的结果很容易出现INF。为了避免这种情形,我们习惯人为地增加一个超级源点。
例如我们现在人为地新增一个0号点(或n+1号点),从它向所有顶点连一条边权为0的边:
现在我们以0号点为源点求各点的最短路即可。注意,这相当于了添加了以下约束条件:
由于
因为这求出的只是一组解,通过简单的数学计算可知,在符合差分约束系统的一组解上加上或减去同一个数,得到的解同样符合原系统。例如,上文例子求得的结果为
其实我们可以把
那么如何求满足
现在给出一道模板题的完整代码:
(洛谷P5960【模板】差分约束算法)
题目描述
给出一组包含 m 个不等式,有 n 个未知数的形如:
的不等式组,求任意一组满足这个不等式组的解。 输入格式
第一行为两个正整数 n,m,代表未知数的数量和不等式的数量。
接下来 m 行,每行包含三个整数,代表一个不等式。输出格式
一行,n 个数,表示的一组可行解,如果有多组解,请输出任意一组,无解请输出NO
。
#include <bits/stdc++.h>
#define MAXN 5005
#define MAXM 10005
using namespace std;
int read()
{
int ans = 0, sgn = 1;
char c = getchar();
while (!isdigit(c))
{
if (c == '-')
sgn *= -1;
c = getchar();
}
while (isdigit(c))
{
ans = ans * 10 + c - '0';
c = getchar();
}
return ans * sgn;
}
int cnt_edge, head[MAXN];
struct
{
int to, next, w;
} edges[MAXM];
void add_edge(int from, int to, int w)
{
edges[++cnt_edge].next = head[from];
edges[cnt_edge].to = to;
edges[cnt_edge].w = w;
head[from] = cnt_edge;
}
bool inqueue[MAXN];
int cnt[MAXN], dis[MAXN];
queue<int> Q;
bool SPFA(int s, int n)
{
memset(dis, 127, sizeof(dis));
// memset(dis, -127, sizeof(dis));
dis[s] = 0;
Q.push(s);
while (!Q.empty())
{
int p = Q.front();
if (cnt[p] > n)
return false;
Q.pop();
inqueue[p] = false;
for (int eg = head[p]; eg != 0; eg = edges[eg].next)
{
int to = edges[eg].to;
if (edges[eg].w + dis[p] < dis[to])
// if (edges[eg].w + dis[p] > dis[to])
{
dis[to] = edges[eg].w + dis[p];
if (!inqueue[to])
{
Q.push(to);
inqueue[to] = true;
cnt[to]++;
}
}
}
}
return true;
}
int main()
{
int n = read(), m = read();
for (int i = 0; i < m; ++i)
{
int x = read(), y = read(), w = read();
add_edge(y, x, w);
// add_edge(x, y, -w);
}
for (int i = 1; i <= n; ++i)
add_edge(0, i, 0);
if (SPFA(0, n))
{
for (int i = 1; i <= n; ++i)
printf("%d ", dis[i]);
}
else
puts("NO");
return 0;
}
这里用到了SPFA判负环,如果存在负环,最短路无解,则原不等式组也无解。
在实际问题中,不等关系不一定这么简单,但有些不等关系可以被转化为
1.
2.
3.
4.
而且题目中还可能隐含一些不等关系,比如:
(洛谷P1250 种树)
题目描述
一条街的一边有几座房子。因为环保原因居民想要在路边种些树。路边的地区被分割成块,并被编号成1..N。每个部分为一个单位尺寸大小并最多可种一棵树。每个居民想在门前种些树并指定了三个号码B,E,T。这三个数表示该居民想在B和E之间最少种T棵树。当然,B≤E,居民必须记住在指定区不能种多于区域地块数的树,所以T≤E-B+l。居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量。
写一个程序完成以下工作: 输入格式
第一行包含数据N,区域的个数(0<N≤30000);
第二行包含H,房子的数目(0<H≤5000);
下面的H行描述居民们的需要:B E T,0<B≤E≤30000,T≤E-B+1。 输出格式
输出文件只有一行写有树的数目
如果以每一块上树的数量为变量,是很难下手的,但我们可以转而使用前缀和。那么题目给出的条件就可以被转化为
https://zhuanlan.zhihu.com/p/105467597zhuanlan.zhihu.com