初涉差分约束系统
一个把数学问题转化为图论模型的很好的例子
差分约束系统
差分约束系统的定义是:一个由$n$个变量和$m$个约束条件组成,形成$m$个形如$x_i-x_j≤k$的不等式($i,j∈[1,n],k$为常数)的系统。
举个例子(图自网络):
例如这个不等式组就是一个差分约束系统。
我们要求的通常是附加几个条件之后(例如$x_i>0$)的这个系统的最大/小解。因为很容易发现,若只有变量之间的相对限制,这个系统要么是无数解;要么是无解。
那么怎么把这个抽象的不等式组化为图论模型呢?
观察一下这个例子(图仍然来自网络):
这个例子其实就是上面那个不等式组的图论描述。其中图中的边$(u,v,w)$表示$x_v-x_u≤w$。
那么当要求最大值时,就求一遍最短路就好了。
看上去是不是很奇怪,为什么求最大值时要求最短路呢?
因为这里的所有约束条件是必须要同时满足的,所以求的最短路就是满足所有条件下的最大值了。
于是就变为了一张图中的最长/短路问题了。
值得注意的是由于差分约束中会有负权,所以应该使用SPFA来解决。
参考:
2.BZOJ 2330: [SCOI2011]糖果 [差分约束系统] 【学习笔记】
一些题目
【线性约束】排队
题目描述
有N个人按顺序排成一条直线,满足任何两个人的位置不重合,现在有两种约束:
X约束Ax Bx Cx(1 <= Ax < Bx <= N),表示Ax和Bx的距离小于等于Cx。
Y约束Ay By Cy(1 <= Ay < By <= N),表示Ay和By的距离大于等于Cy。
如果这样的排列存在,输出总和最长距离(无限则输出INF)。
否则如果不存在,输出-1。
题目分析
建图的要点上文已经提到过了,这里讲一下怎么判断无限长。
这里求的是最大值,也就是说我们跑的是最短路。没有最大值等价于没有最短路。
显然没有最短路就是存在负权环,刚好SPFA顺带解决了这个问题。
【区间约束】luoguP1645 序列
题目描述
有一个整数序列,它的每个数各不相同,我们不知道它的长度是多少(即整数个数),但我们知道在某些区间中间至少有多少个整数,用区间(Li,Ri,Ci)来描述,表示这个整数序列中至少有Ci个数来自区间[Li,Ri],给出若干个这样的区间,问这个整数序列的长度最少能为多少?
输入输出格式
输入格式:
第一行一个整数N,表示区间个数;
接下来N行,每行三个整数(Li,Ri,Ci),描述一个区间。
输出格式:
仅一个数,表示该整数序列的最小长度。
【数据规模】
N<=1000,0<=Li<=Ri<=1000,1<=Ci<=Ri-Li+1
题目大意
这里讲一下大致题意(好像是出题人语文不大好写的不是人话?也可能是我语文不好看不懂?)
现在有一个长为1000单位长度的区间,单位长度内要么有一个数,要么什么都没有。
现在知道一些小区间内至少有多少数,求大区间内最少有多少数。
题目分析
区间问题套路:前缀和。
我们对于前缀和进行差分约束。
这里求的是最小值=最长路。
1 #include<bits/stdc++.h> 2 const int maxn = 1013; 3 4 struct node 5 { 6 int y,val; 7 node() {} 8 node(int a, int b):y(a),val(b) {} 9 }; 10 int n,m; 11 int dis[maxn]; 12 bool vis[maxn]; 13 std::vector<node> a[maxn]; 14 std::queue<int> q; 15 16 int read() 17 { 18 char ch = getchar(); 19 int num = 0; 20 for (; !isdigit(ch); ch = getchar()); 21 for (; isdigit(ch); ch = getchar()) 22 num = (num<<1)+(num<<3)+ch-48; 23 return num; 24 } 25 void spfa(int s) 26 { 27 q.push(s); 28 memset(dis, -63, sizeof dis); 29 dis[s] = 0; 30 while (q.size()) 31 { 32 int tt = q.front(); 34 vis[tt] = 0; 35 q.pop(); 36 for (unsigned int i=0; i<a[tt].size(); i++) 37 if (dis[a[tt][i].y] < dis[tt]+a[tt][i].val) //最长路 38 { 39 dis[a[tt][i].y] = dis[tt]+a[tt][i].val; 40 if (!vis[a[tt][i].y]){ 41 vis[a[tt][i].y] = 1; 42 q.push(a[tt][i].y); 43 } 44 } 45 } 46 } 47 int main() 48 { 49 m = read(); 50 for (int i=1; i<=m; i++) 51 { 52 int x = read()+1, y = read()+1 53 , z = read(); 54 n = std::max(n, y); 55 a[x-1].push_back(node(y, z)); 56 } 57 for (int i=1; i<=n; i++) 58 a[i-1].push_back(node(i, 0)), //相邻前缀和之间也有限制 59 a[i].push_back(node(i-1, -1)); //这是为了避免f[i]=f[i-1]+k(k>1)的情况 60 spfa(0); 61 printf("%d\n",dis[n]); 62 return 0; 63 }
【区间约束】bzoj2330: [SCOI2011]糖果
Description
幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww需要满足小朋友们的K个要求。幼儿园的糖果总是有限的,lxhgww想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
Input
输入的第一行是两个整数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个小朋友分到的糖果;
Output
输出一行,表示lxhgww老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出-1。
【数据范围】
对于30%的数据,保证 N<=100
对于100%的数据,保证 N<=100000
对于所有的数据,保证 K<=100000,1<=X<=5,1<=A, B<=N
题目分析
看上去稍微复杂了一点?
这里出现了严格的不等式,不过由于这里的所有值都是整数,我们可以将$a<b$变为$a≤b-1$。
再者在建图的地方注意一下、判断一下无解,就可以了。
这里有一篇写得非常好的博客:BZOJ 2330: [SCOI2011]糖果 [差分约束系统] 【学习笔记】,里面有一些非常妙的注意点。
1 #include<bits/stdc++.h> 2 const int maxn = 100033; 3 4 struct node 5 { 6 int y,val; 7 node() {} 8 node(int a, int b):y(a),val(b) {} 9 }; 10 int n,k; 11 int dis[maxn],tot[maxn]; 12 long long cnt; 13 bool vis[maxn]; 14 std::queue<int> q; 15 std::vector<node> a[maxn]; 16 17 int read() 18 { 19 char ch = getchar(); 20 int num = 0; 21 for (; !isdigit(ch); ch = getchar()); 22 for (; isdigit(ch); ch = getchar()) 23 num = (num<<1)+(num<<3)+ch-48; 24 return num; 25 } 26 void end() 27 { 28 puts("-1"); 29 exit(0); 30 } 31 bool spfa() 32 { 33 for (int i=1; i<=n; i++) 34 { 35 vis[i] = 1, dis[i] = 1, tot[i] = 1; 36 q.push(i); 37 } 38 while (q.size()) 39 { 40 int tt = q.front(); 41 vis[tt] = 0; 42 q.pop(); 43 for (unsigned int i=0; i<a[tt].size(); i++) 44 { 45 int v = a[tt][i].y, w = a[tt][i].val; 46 if (dis[v] < dis[tt]+w) 47 { 48 dis[v] = dis[tt]+w; 49 if (!vis[v]){ 50 vis[v] = 1; 51 if (++tot[v] > n) return 1; 52 q.push(v); 53 } 54 } 55 } 56 } 57 return 0; 58 } 59 int main() 60 { 61 n = read(), k = read(); 62 for (int i=1; i<=k; i++) 63 { 64 int tt = read(), x = read(), y = read(); 65 if (tt==1 || tt==3) 66 a[y].push_back(node(x, 0)); 67 if (tt==1 || tt==5) 68 a[x].push_back(node(y, 0)); 69 if (tt==2) 70 { 71 if (x==y) end(); 72 else a[x].push_back(node(y, 1)); 73 } 74 if (tt==4) 75 { 76 if (x==y) end(); 77 else a[y].push_back(node(x, 1)); 78 } 79 } 80 if (spfa()) end(); 81 for (int i=1; i<=n; i++) cnt += dis[i]; 82 printf("%lld\n",cnt); 83 return 0; 84 }
【未知条件约束】poj1275Cashier Employment
题目链接:http://poj.org/problem?id=1275
现在还不会……
END