现在有三个不等式:
begin{cases}
B-A leq k_1 \
C-B leq k_2 \
C-A leq k_3
end{cases}
求出
C
−
A
C-A
C−A的最大值。
将前两个不等式相加,得到
C
−
A
≤
k
1
+
k
2
C-A \leq k_1+k_2
C−A≤k1+k2
C
−
A
C-A
C−A需要同时满足
begin{cases}
C-A leq k_1+k_2 \
C-A leq k_3
end{cases}
所以答案为:
m
i
n
(
k
1
+
k
2
,
k
3
)
min(k_1+k_2,k_3)
min(k1+k2,k3)
我们考虑用这三个不等式建立一个图,
B
−
A
l
e
q
k
1
B-A leq k_1
B−Aleqk1 表示有一条边
(
A
,
B
)
(A,B)
(A,B),边权为
k
1
k_1
k1;
C
−
B
l
e
q
k
2
C-B leq k_2
C−Bleqk2 表示有一条边
(
B
,
C
)
(B,C)
(B,C),边权为
k
2
k_2
k2;
C
−
A
l
e
q
k
3
C-A leq k_3
C−Aleqk3 表示有一条边
(
A
,
C
)
(A,C)
(A,C),边权为
k
3
k_3
k3,如下图:
答案为
m
i
n
(
k
1
+
k
2
,
k
3
)
min(k_1+k_2,k_3)
min(k1+k2,k3),其实就是找A到C的最短的一条路。
差分约束系统的概念
差分约束系统是一种特殊的
N
N
N元一次不等式组,包含
N
N
N个变量
X
1
X
N
X_1~X_N
X1 XN以及
M
M
M个约束条件,每个约束条件是由两个变量作差构成的,如
X
i
−
X
j
l
e
q
C
k
X_i-X_j leq C_k
Xi−XjleqCk ,
X
i
−
X
j
>
C
k
X_i-X_j > C_k
Xi−Xj>Ck(C_k为常数)等。
差分约束系统要么无解,要么有无数组解。
如果
a
1
,
a
2
,
a
3
.
.
.
a
N
{a_1,a_2,a_3...a_N}
a1,a2,a3...aN是一组解,那么
a
1
+
d
,
a
2
+
d
,
a
3
+
d
.
.
.
a
N
+
d
{a_1+d,a_2+d,a_3+d...a_N+d}
a1+d,a2+d,a3+d...aN+d也是一组解,作差后
d
d
d恰好消掉。
差分约束系统与最短路
在一个图中,存在一条边
(
u
,
v
)
(u,v)
(u,v),边权为
W
u
−
>
v
W_{u->v}
Wu−>v,源点到点
u
,
v
u,v
u,v的最短路为
d
i
s
[
u
]
,
d
i
s
[
v
]
dis[u],dis[v]
dis[u],dis[v].
那么一定存在不等式:
d
i
s
[
v
]
l
e
q
d
i
s
[
u
]
+
W
u
−
>
v
dis[v] leq dis[u]+W_{u->v}
dis[v]leqdis[u]+Wu−>v,
d
i
s
[
v
]
dis[v]
dis[v]已经是最小值了,从点u经过边
(
u
,
v
)
(u,v)
(u,v),一定是大于等于
d
i
s
[
v
]
dis[v]
dis[v]。
将不等式移项,可得:
d
i
s
[
v
]
−
d
i
s
[
u
]
l
e
q
W
u
−
>
v
dis[v]-dis[u] leq W_{u->v}
dis[v]−dis[u]leqWu−>v。
因此对于不等式
X
i
−
X
j
l
e
q
W
k
X_i-X_j leq W_k
Xi−XjleqWk,可以看作存在一条
X
i
X_i
Xi到
X
j
X_j
Xj的边,边权为
W
k
W_k
Wk。对于不等式组,就可以建立一个图,通过求出源点到其他节点的最短路
d
i
s
dis
dis,就是最后的答案。
接下来,我们以下面这个不等式组为例:
begin{cases}
X_3-X_1 leq 5 \
X_4-X_3 leq -3 \
X_4-X_1 leq 4 \
X_5-X_4 leq 0 \
X_2-X_5 leq 1 \
X_1-X_5 leq -1 \
end{cases}
根据不等式组可以建立一个图:
接下来就是求解最短路了。但是,问题来了,哪一个是源点呢?
事实上,任何一个点作为源点都是可以的:
(1)以
X
1
X_1
X1为源点,求解出
d
i
s
[
X
1
]
=
0
,
d
i
s
[
X
2
]
=
3
,
d
i
s
[
X
3
]
=
5
,
d
i
s
[
X
4
]
=
2
,
d
i
s
[
X
5
]
=
2
dis[X_1]=0,dis[X_2]=3,dis[X_3]=5,dis[X_4]=2,dis[X_5]=2
dis[X1]=0,dis[X2]=3,dis[X3]=5,dis[X4]=2,dis[X5]=2,带入到不等式组中,发现是满足所有不等式的;
(2)以
X
4
X_4
X4为源点,求解出
d
i
s
[
X
1
]
=
−
1
,
d
i
s
[
X
2
]
=
1
,
d
i
s
[
X
3
]
=
5
,
d
i
s
[
X
4
]
=
0
,
d
i
s
[
X
5
]
=
0
dis[X_1]=-1,dis[X_2]=1,dis[X_3]=5,dis[X4]=0,dis[X_5]=0
dis[X1]=−1,dis[X2]=1,dis[X3]=5,dis[X4]=0,dis[X5]=0,带入到不等式组中,发现是满足所有不等式的;
如果以
X
2
X_2
X2为源点,发现无法到达起点结点,无解。然而,是存在解的,如何处理这种情况呢?
最朴素的做法,就是列举每一个点作为源点,求最短路,但是总的时间复杂度需要乘以
N
N
N(
N
N
N为结点数目)。
当遇到这种类似的情况,有一个常用的建模思想——超级源点。
超级源点
超级源点就是构造一个点,从这个点出发,对每一个点连接一条边权为0的边。
假设存在U到V的最短路:U->…->V,从超级源点出发:超级源点->U->…->V,可以发现,边权为0,所以对最短路没有任何影响,但是这样确保从超级源点出发,能够访问到每一个结点。
其实,也相当于添加了如下不等式组:
begin{cases}
X_1-X_0 leq 0 \
X_2-X_0 leq 0 \
X_3-X_0 leq 0 \
X_4-X_0 leq 0 \
X_5-X_0 leq 0 \
end{cases}
因为 d i s [ X 0 ] = 0 dis[X_0]=0 dis[X0]=0,即添加了限制条件:
begin{cases}
X_1leq 0 \
X_2leq 0 \
X_3leq 0 \
X_4leq 0 \
X_5leq 0 \
end{cases}
以
X
0
X_0
X0为超级源点,求解出
d
i
s
[
X
1
]
=
−
4
,
d
i
s
[
X
2
]
=
−
2
,
d
i
s
[
X
3
]
=
0
,
d
i
s
[
X
4
]
=
−
3
,
d
i
s
[
X
5
]
=
−
3
dis[X_1]=-4,dis[X_2]=-2,dis[X_3]=0,dis[X_4]=-3,dis[X_5]=-3
dis[X1]=−4,dis[X2]=−2,dis[X3]=0,dis[X4]=−3,dis[X5]=−3,
此时,求解出的即是
l
e
q
0
leq 0
leq0的一组最大解:
因为对于每一个点,如:
begin{cases}
Xleq k_1 \
...\
Xleq 0 \
end{cases}
既要满足 l e q 0 leq 0 leq0,又要满足其他不等式,通过最短路求解出的就是 l e q 0 leq 0 leq0的最大值。
无解
当不等式组建立的图存在负权环,即没有最短路,此时不等式组无解。
解决问题
1. 求解X-Y的最大值
求解X-Y的最大值,不等式组应该为
A
−
B
l
e
q
K
A-Bleq K
A−BleqK(
K
K
K为常数)形式,只有leq才能取到最大值。
如果存在geq,那么乘以-1进行转换即可。
求解X-Y的最大值,可以联系开篇的例子,实际上就是求解以X为源点,到Y的最短路。
2. 求解X-Y的最小值
求解X-Y的最大值,不等式组应该为
A
−
B
g
e
q
K
A-Bgeq K
A−BgeqK(
K
K
K为常数)形式,只有geq才能取到最大值。
对于最小值,可以求解最长路。
在一个图中,存在一条边
(
u
,
v
)
(u,v)
(u,v),边权为
W
u
−
>
v
W_{u->v}
Wu−>v,源点到点
u
,
v
u,v
u,v的最长路为
d
[
u
]
,
d
[
v
]
d[u],d[v]
d[u],d[v].
那么一定存在不等式:
d
i
s
[
v
]
g
e
q
d
i
s
[
u
]
+
W
u
−
>
v
dis[v] geq dis[u]+W_{u->v}
dis[v]geqdis[u]+Wu−>v,
d
[
v
]
d[v]
d[v]已经是最大值了,从点u经过边
(
u
,
v
)
(u,v)
(u,v),一定是小于于等于
d
[
v
]
d[v]
d[v]。
求解X-Y的最大值,实际上就是求解以X为源点,到Y的最长路。
3. 求解满足条件的一组解
对于不等式:
begin{cases}
X_3-X_1 leq 5 \
X_4-X_3 leq -3 \
X_4-X_1 leq 4 \
X_5-X_4 leq 0 \
X_2-X_5 leq 1 \
X_1-X_5 leq -1 \
end{cases}
通过添加超级源点,向每个结点连一条边权为0的边,实际上求解出的就是
l
e
q
0
leq 0
leq0的一组最大解。
例子:【SCOI 2011】糖果
【题目描述】
幼儿园里有 N 个小朋友,lxhgww 老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww 需要满足小朋友们的 K 个要求。幼儿园的糖果总是有限的,lxhgww 想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
【输入】
输入的第一行是两个整数 N,K。接下来 K 行,表示这些点需要满足的关系,每行 3 个数字,X,A,B。
如果 X=1, 表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多;
如果 X=2, 表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果;
如果 X=3, 表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果;
如果 X=4, 表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果;
如果 X=5, 表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果;
【输出】
输出一行,表示 lxhgww 老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 −1。
【样例输入】
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
【样例输出】
11
【数据范围】
对于
30
30%
30 的数据,保证 $N leq 100 $
对于
100
100%
100 的数据,保证 $N leq 100000 $
对于所有的数据,保证 $K leq 100000,1 leq X leq 5,1 leq A,B leq N $
【题目分析】
(1)如果 X=1, 表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多;
一样多,我们可以看作
A
−
B
l
e
q
0
,
B
−
A
g
e
q
0
A-B leq 0,B-A geq 0
A−Bleq0,B−Ageq0,只有A==B,才满足两个不等式。
(2)如果 X=2, 表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果;
看作
A
<
B
+
0
A < B+0
A<B+0,转换为
A
l
e
q
B
−
1
A leq B-1
AleqB−1
(3)如果 X=3, 表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果;
看作
A
g
e
q
B
+
0
A geq B+0
AgeqB+0
(4)如果 X=4, 表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果;
看作
A
>
B
+
0
A > B+0
A>B+0,转换为
A
g
e
q
B
+
1
A geq B+1
AgeqB+1
(5)如果 X=5, 表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果;
看作
A
l
e
q
B
+
0
A leq B+0
AleqB+0
同时,题目要求每个小朋友都要分到糖果,即所有分到的糖果数都要 $ geq 1$。即添加不等式组:
begin{cases}
A geq S+1 \
B geq S+1 \
C geq S+1 \
...
end{cases}
将S作为超级源点,向每一个结点连接一条边权为1的边(
d
[
S
]
=
0
d[S]=0
d[S]=0)。此时跑一边最长路,就得到答案了。
那么能不能将不等式转换之后,跑最短路呢?转换为:
begin{cases}
S leq A-1 \
S leq B-1 \
S leq C-1 \
...
end{cases}
这种做法是错误的哦,注意,超级源点是一个假象点,不能有其他结点向超级源点连边。同时,求解的是最少糖果数,最小值,不等式显然要为$ geq $。
#include<bits/stdc++.h>
#define N 100100
#define M 200100
using namespace std;
int n,m,s,vis[N],cnt[N];
int first[N],to[2*M],nex[2*M],t;
long long dis[N],ans,w[2*M];
queue <int> q;
void Add(int u,int v,long long W)
{
nex[++t]=first[u];
first[u]=t;
to[t]=v;
w[t]=W;
}
void SPFA(int k)
{
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)
dis[i]=-9999999;
q.push(k);
dis[k]=0;
vis[k]=1;
cnt[k]++;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=first[u];i!=-1;i=nex[i])
{
int v=to[i];
if(dis[u]+w[i]>dis[v])
{
dis[v]=dis[u]+w[i];
if(!vis[v])
{
q.push(v);
vis[v]=1;
cnt[v]++;
if(cnt[v]>n) {cout<<-1;exit(0);}
}
}
}
}
}
int main()
{
//freopen("candy6.in","r",stdin);
memset(first,-1,sizeof(first));
scanf("%d%d",&n,&m);
int x,y,z;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
if(x==1) {Add(y,z,0);Add(z,y,0);}
if(x==2) Add(y,z,1);
if(x==3) Add(z,y,0);
if(x==4) Add(z,y,1);
if(x==5) Add(y,z,0);
if((x==2||x==4)&&y==z) {printf("-1n");return 0;}
}
for(int i=n;i>=1;i--)
Add(0,i,1);
SPFA(0);
for(int i=1;i<=n;i++)
ans+=dis[i];
cout<<ans<<endl;
return 0;
}
4.其他情况
( 1 ) 当需要求解X-Y的最大值,但是给出的是形如
A
−
B
g
e
q
K
A-Bgeq K
A−BgeqK(
K
K
K为常数)的不等式组。
解决方法:两边同时乘以
−
1
-1
−1,转换后得到
B
−
A
l
e
q
−
K
B-Aleq -K
B−Aleq−K
( 2 ) 如果给出的是形如
A
−
B
<
K
A-B < K
A−B<K(
K
K
K为常数)的不等式组:
解法方法:转换为
A
−
B
l
e
q
K
−
1
A-Bleq K-1
A−BleqK−1
总结
差分约数系统是用来求解不等式组的一组解,应用面比较窄,只能是不等式,通过将不等式建图后求解最长路或最短路。
小于等于求最短路,大于等于求最长路.
超级源点的建模思想需要大家记住,后面也会经常用到。