什么是差分约束系统?
差分约束系统,是一类关于不等式组的线性规划问题
比如:
给出n个形如
a−b≤c
的不等式,求一组任意解
一般可以转化为最短路问题求解。
差分约束系统的转化
我们以
Xi−Yi≤Zi
为例
观察发现,其实这个不等式与最短路问题中的“三角不等式”相似:
Dist(i)+W(i,j)≥Dist(j)
Dist(j)−Dist(i)≤W(i,j)
那么是否可以转化为最短路呢?它的实际意义是什么?
三角不等式的意义:
对于求解最短路后的图,选取任意两个有边相连的点,必有
Dist(i)+W(i,j)≥Dist(j)
这意味着:只要存在一条边
(i,j)
,利用最短路算法,必然会得到符合
Dist(j)−Dist(i)≤W(i,j)
的变量取值
差分约束系统不就是要求符合
Xi−Yi≤Zi
的变量取值吗!
于是,对于约束条件
Xi−Yi≤Zi
,我们可以直接建边
(Yi,Xi)
然后刷最短路!
就这样解决了!
刷最短路的起点是随意的,因为这不影响任何过程。
刷完最短路只是获得了其中一组解,
题目一般会要求具有某些特征的解,把可行解的所有变量都加上同一个值就可以了
几个小技巧
- 前面只阐述了 Xi−Yi≤Zi 型的问题,其实如果把 ≤ 换成 ≥ ,只需要刷最长路就可以了
- 图有可能是不联通的,一般可以建0点,0点向每个点连一条边权为0的边。然后以0为起点
- 差分约束系统的无解其实对应着最短路问题的无解(负权回路),SPFA判一下即可
- 对于不等式中不带等号的问题,一般答案都是整数,那么就可以通过+1的方法来变成带等号的不等式
经典例题
POJ1201
经典的差分约束系统题型……
首先看到要求最小值,那么显然要刷最长路
即
x−y≥z
型约束
构造前缀和
Si
那么对于一个条件
(a,b,c)
,则有
Sb−Sa≥c
值得注意的是,前缀和的单调性 以及 一个位置对答案的贡献最大为1
这两个隐藏条件容易被忽视
即
Si−Si−1≥0
,
Si−1−Si≥−1
然后就没有辣~~
示例程序:
#include<cstdio>
#include<cstring>
#define nc getchar
#define cl(x,y) memset(x,y,sizeof(x))
inline int red(){
int res=0,f=1;char ch=nc();
while (ch<'0'||'9'<ch) {if (ch=='-') f=-f;ch=nc();}
while ('0'<=ch&&ch<='9') res=res*10+ch-48,ch=nc();
return res*f;
}
const int maxn=50005,maxe=150005,INF=0x3f3f3f3f;
int n;
int tot,lnk[maxn],nxt[maxe],son[maxe],w[maxe];
inline void add(int x,int y,int z){
son[++tot]=y;nxt[tot]=lnk[x];lnk[x]=tot;w[tot]=z;
}
int que[maxn],dst[maxn];
bool vis[maxn];
void spfa(){
cl(vis,0);cl(dst,192);
int hed=0,til=1;
que[1]=0;dst[0]=0;
while (hed!=til){
int x=que[hed=(hed+1)%maxn];
vis[x]=0;
for (int j=lnk[x];j;j=nxt[j])
if (dst[son[j]]<dst[x]+w[j]){
dst[son[j]]=dst[x]+w[j];
if (!vis[son[j]])
vis[son[j]]=1,que[til=(til+1)%maxn]=son[j];
}
}
}
int main(){
n=red();
for (int i=0;i<=50000;i++) add(i,i+1,0),add(i+1,i,-1);
for (int i=1,l,r,c;i<=n;i++)
l=red()+1,r=red()+1,c=red(),add(l-1,r,c);
spfa();
printf("%d",dst[50001]);
return 0;
}