差分约束系统解决的是一个N元1次不等式求解的问题,其中每一个不等式都形如Xi-Xj<=Ck,其中Ck为常数。差分约束系统与最短路关系甚密,可以用最短路算法求解,即先对不等式进行移项操作,将不等式转化为Xi<=Xj+Ck,这正是求完单源最短路后应该满足的条件(在没有负环的情况下)。其中每一个变量可以看作距源点的距离,常量就可以看作边权,每一个不等式就可以建一条边。
差分约束系统求解的技巧和注意事项:
1)考虑以下不等式组:a>b,b>c,c>a。很明显,这个不等式组无解。那么如何判断一个差分约束系统无解呢?联系上文的差分约束系统建图规则,当我们发现了一个负环的时候,差分约束系统就无解了。
2)在有不等号方向不同的不等式时,要先通过移项将所有不等式转化为方向相同的不等式。
3)在全都是>=的不等式组时,如Xi>=Xj+Ck,建边时需要建一条W(j,i)=Ck的边,再通过单源最长路进行求解。
4)在全都是<=的不等式组时,如Xi<=Xj+Ck,建边时需要建一条W(j,i)=Ck的边,再通过单源最短路进行求解。
5)在没有负环的情况下,每个Xi=dist[i]+k(k>=0)就是差分约束系统的一组解。
6)可能出现负边权,需要使用SPFA算法
下面来一道例题:
这道题目就是一道经典的差分约束系统题目,题目中给定了三种差分约束的条件:
- 农场a比农场b至少多种植了c个单位的作物,
- 农场a比农场b至多多种植了c个单位的作物,
- 农场a与农场b种植的作物数一样多。
对于这三个约束条件,我们可以列出如下不等式:
1、对于条件1,易得Xa-Xb>=c,进而得到Xa>=Xb+c;建一条W(b,a)=c的边
2、对于条件2,易得Xa-Xb<=c,进而得到-Xb<=c-Xa,不等式两边同乘-1得到Xb>=Xa+(-c);建一条W(a,b)=-c的边;
3、对于条件3,建两条W(a,b)=W(b,a)=0的边。
建好边后判断有无负环即可,下面给出参考代码(此题基本相当于差分约束系统模板题):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 int n,m,dist[50005],v[50005],w[50005],nxt[50005],head[50005],cnt,c,x,y,z; 5 bool instack[50005]; 6 void add(int a,int b,int c) 7 { 8 v[++cnt]=b; 9 w[cnt]=c; 10 nxt[cnt]=head[a]; 11 head[a]=cnt; 12 } 13 bool spfa(int node) 14 { 15 instack[node]=1; 16 for(int i=head[node];i;i=nxt[i]) 17 { 18 int y=v[i]; 19 if(dist[y]<dist[node]+w[i]) 20 { 21 dist[y]=dist[node]+w[i]; 22 if(instack[y])return 0; 23 if(!spfa(y))return 0; 24 } 25 } 26 instack[node]=0; 27 return 1; 28 } 29 int main() 30 { 31 cin>>n>>m; 32 for(int i=1;i<=m;i++) 33 { 34 cin>>c; 35 if(c==1) 36 { 37 cin>>x>>y>>z; 38 //dist[x]>=z+dist[y] 39 add(y,x,z); 40 } 41 if(c==2) 42 { 43 cin>>x>>y>>z; 44 //dist[y]>=-z+dist[x] 45 add(x,y,-z); 46 } 47 if(c==3){ 48 cin>>x>>y; 49 add(x,y,0); 50 add(y,x,0); 51 } 52 } 53 for(int i=1;i<=n;i++)add(0,i,0); 54 for(int i=1;i<=n;i++)dist[i]=-21370444; 55 if(spfa(0))cout<<"Yes"; 56 else cout<<"No"; 57 return 0; 58 }