初涉差分约束系统

一个把数学问题转化为图论模型的很好的例子

差分约束系统

差分约束系统的定义是:一个由$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来解决。

参考:

1.夜深人静写算法(四) - 差分约束

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

posted @ 2018-05-16 20:17  AntiQuality  阅读(194)  评论(0编辑  收藏  举报