差分约束系统

差分约束系统解决的是一个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的边。

建好边后判断有无负环即可,下面给出参考代码(此题基本相当于差分约束系统模板题):

 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 }
View Code
原文地址:https://www.cnblogs.com/szmssf/p/11079511.html