差分约束系统
差分约束系统只是对最短路算法的一种应用,没有什么新的算法,只是对于具体问题的建图方法的确定
差分约束系统解决的问题是不等式组的求解:
X1 - X2 <= 0
X1 - X5 <= -1
X2 - X5 <= 1
X3 - X1 <= 5
X4 - X1 <= 4
X4 - X3 <= -1
X5 - X3 <= -3
X5 - X4 <= -3
这就是一个不等式组,给出的不等式组只存在小于等于号,如果有个别的式子是大于等于,我们可以通过两边同时乘-1的得到统一的不等号方向的不等式组。
这个不等式组一定是无解和无数解两种情况,因为如果是存在任意一组解,{x1,x2,x3,x4,x5},我们都可以通过{x1+k,x2+k,x3+k,x4+k,x5+k}得到一个新的解。所以解的个数是无数的。
因为每个数都加k,他们任意两个数之间的差是不变的,所以对于不等式没有影响。
与最短路联系
B - A <= c (1)
C - B <= a (2)
C - A <= b (3)
如果要求C-A的最大值,可以知道max(C-A)= min(b,a+c),而这正对应了下图中C到A的最短路。
差分约束建图技巧
1.对于一个全部都是<=号的不等式组,我们可以将每个式子转化为Xi<=Xj+W(i,j),那么就建一条边,Xj->Xi=W(i,j),然后利用最短路径解决问题,在x0定死的情况下,求得最小值
2.对于一个全部都是>=号的不等式组,我们可以将每个式子转化为Xi>=Xj+W(i,j),那么就建一条边,Xj->Xi=W(i,j),然后利用最长路径解决问题,在x0定死的情况下,求得最大值
如果dis[Xi]为inf或-inf,那么Xi为任意解
如果求最短路的过程中出现负环,那么说明该不等式组无解
来看看一道例题
【代码实现】
1 #include <cstdio> 2 #include <algorithm> 3 #include<queue> 4 #include<cstring> 5 using namespace std; 6 const int maxn=1e5+5; 7 const int INF=1e9+7; 8 struct sd{ 9 int to,ww,next; 10 }edge[maxn<<1]; 11 queue<int> que; 12 int dis[maxn],num[maxn],head[maxn],n,m,cnt; 13 bool vis[maxn]; 14 void add_edge(int a,int b,int ww) 15 { 16 edge[++cnt].next=head[a]; 17 edge[cnt].to=b; 18 edge[cnt].ww=ww; 19 head[a]=cnt; 20 } 21 bool spfa() 22 { 23 for(int i=1;i<=n;i++) 24 dis[i]=1,vis[i]=1,que.push(i); 25 while(!que.empty()) 26 { 27 int v=que.front();que.pop();vis[v]=0; 28 for(int i=head[v];i;i=edge[i].next) 29 { 30 int to=edge[i].to; 31 if(dis[to]<dis[v]+edge[i].ww) 32 { 33 dis[to]=dis[v]+edge[i].ww; 34 if(!vis[to]) 35 { 36 if(++num[to]>n) return false; 37 vis[to]=1,que.push(to); 38 } 39 } 40 } 41 } 42 return true; 43 } 44 int main() 45 { 46 int ord,a,b; 47 long long ans=0; 48 scanf("%d%d",&n,&m); 49 for(int i=1;i<=m;i++) 50 { 51 scanf("%d%d%d",&ord,&a,&b); 52 if(ord==1) add_edge(a,b,0),add_edge(b,a,0); 53 if(ord==2) {if(a==b){printf("-1");return 0;}add_edge(a,b,1);} 54 if(ord==3) add_edge(b,a,0); 55 if(ord==4) {if(a==b){printf("-1");return 0;}add_edge(b,a,1);} 56 if(ord==5) add_edge(a,b,0); 57 } 58 if(!spfa()) {printf("-1");return 0;} 59 for(int i=1;i<=n;i++) ans+=dis[i]; 60 printf("%lld",ans); 61 return 0; 62 }