差分约束系统
先总结下:
第一:
感觉难点在于建图
第二:
①:对于差分不等式,a - b <= c ,建一条 b 到 a 的权值为 c 的边,求的是最短路,得到的是最大值
②:对于不等式 a - b >= c ,建一条 b 到 a 的权值为 c 的边,求的是最长路,得到的是最小值
③:存在负环的话是无解
④:求不出最短路(dist[ ]没有得到更新)的话是任意解
第三:
一种建图方法:
设x[i]是第i位置(或时刻)的值(跟所求值的属性一样),那么把x[i]看成数列,前n项和为s[n],则x[i] = s[i] - s[i-1];
那么这样就可以最起码建立起类似这样的一个关系:0 <= s[i] - s[i-1] <= 1;
其他关系就要去题目探索了
第一题意:
输入n个学生和m个比较关系:a,b,c表示学生a认为学生b得到的糖果不能比他多c。问你学生1和学生n之间糖果数量最大是多少?
分析:直接按b-a<=c来建图即可。
第二题意:
给你一个序列的起点 Si 和序列的长度ni,以及他们的区间和与给定的数 ki 的大小关系,然后问你是否存在这样的关系?
分析:(1) 给的不是序列的起点和终点,而是起点和序列长度,所以区间为 [Si, Si+n],这里容易出错。
(2) 要把 > 和 < 关系,通过 Ki-1 变为 <= 和 >= 的关系。
(3) Si +Si+1+.....+ Si+n < ki 可转化为 Sum(i+n) - Sum(i-1) < Ki 从而满足差分约束系统。
(4) 题目中没有指明源点,故设置n+1为超级源点,从超级源点跑spfa即可。
1 tot = 0; 2 _Clr(head, -1); 3 while(m--) 4 { 5 scanf("%d%d%s%d", &a, &b, p, &c); 6 if(p[0]=='g') 7 Add_edge(a+b, a-1, -c-1); 8 else 9 Add_edge(a-1, a+b, c-1); 10 } 11 int S = n+1; 12 for(int i=0; i<=n; i++) 13 Add_edge(S, i, 0);
第三题意:
给你n个整数区间 [a,b]。让你找一个最小的集合Z,使得Z与每个区间的交集都至少有两个不同的元素。问你Z的大小最小是多少?
分析: 用Si表示区间[0, i]的整数个数,由题可知,任一个区间 [a, b] 最少应包含两个元素;即 Sb-Sa-1>=2 。
还有一个隐含条件:因为是整数区间,那么任一相邻的两个区间Si和Si-1满足关系:0 <= Si-Si-1 <= 1。
还要添加一个源点,让你求最小值,所以求最长路即可。
1 tot = 0; 2 _Clr(head, -1); 3 while(m--) 4 { 5 scanf("%d%d", &a, &b); 6 Add_edge(a, b+1, 2); 7 n = max(n, b+1); 8 } 9 S = n+3; // 源点S=n+2也是一样的 10 for(int i=0; i<n; i++) 11 { 12 Add_edge(i+1, i, -1); 13 Add_edge(i, i+1, 0); 14 Add_edge(S, i, 0); 15 }
第四题意:
和上提基本一样,就是交集不再为2,而是 ci,起点和终点是整个区间的最左边的点和最右边的点。
1 tot = 0; 2 _Clr(head, -1); 3 for(int i=0; i<n; i++) 4 { 5 scanf("%d%d%d", &a, &b, &c); 6 Add_edge(a, b+1, c); 7 st = min(a, st); 8 ed = max(b+1, ed); 9 } 10 for(int i=st; i<=ed; i++) 11 { 12 Add_edge(i, i+1, 0); 13 Add_edge(i+1, i, -1); 14 }
第五题意:
有n头奶牛排成一队,他们都喜欢尽量和关系好的靠的进些,和关系坏的离的远些(允许多个奶牛站在同一个点)。ml个友好关系 a,b,d表示a,b之间最远只能隔d; 和md个反感关系 a,b,d表示a,b之间至少要相隔d。问你奶牛1和奶牛n之间的最大距离是多少?如果无法排成一队输出-1,如果他们之间可以无限大则输出-2。
分析:用 Si 表示 i 和 1 之间的距离,那么建图关系已经很明显了,不过,还有一个比较隐含的条件:Si-Si-1>=0。因为相邻两个之间可以站同一个位置。
1 tot = 0; 2 _Clr(head, -1); 3 for(int i=0; i<ml; i++) 4 { 5 scanf("%d%d%d", &a, &b, &c); 6 Add_edge(a, b, c); 7 } 8 for(int i=0; i<md; i++) 9 { 10 scanf("%d%d%d", &a, &b, &c); 11 Add_edge(b, a, -c); 12 } 13 for(int i=1; i<=n; i++) // 不过,去掉这个隐含条件也能AC。唉..... 14 Add_edge(i+1, i, 0)
第六题意:
还不会做,先留着。。。。。
第七题意:
n个岗哨,m个信息。信息有两个格式:P A B X 表示岗哨 A 在岗哨 B 北边 X 光年处;V A B 表示岗哨 A 在岗哨 B 北边至少 1 光年处。然后,让你确定这m条信息是否是可靠的或不可靠的?
分析: 用 S(i) 表示岗哨 i 在岗哨 S 北边的距离。那么对于P A B X就有 S(A) - S(B) = X;可以转化为:S(A)-S(b)>=X && S(A)-S(B)<=X 来建图。
S 是自己设立的源点。
1 tot = 0; 2 _Clr(head, -1); 3 while(m--) 4 { 5 scanf(" %c%d%d", &p, &a, &b); 6 if(p=='P') 7 { 8 scanf("%d", &c); 9 Add_edge(b, a, c); 10 Add_edge(a, b, -c); 11 } 12 else 13 Add_edge(a, b, -1); 14 } 15 for(int i=1; i<=n; i++) 16 Add_edge(0, i, 0);
第八题意:
有个忍者在一排树上联系跳跃,他严格的按照树的高度从矮到高的顺序跳,但是他跳的距离有个限制 D。问你最矮的树和最高的树之间的最长距离是多少?
分析:有一个需要的注意就是他是从左往右跳的还是从右往左跳的?还有一个隐含限制条件:每棵树都是在一个整数点,也就是两颗树之间距离最近为1,即 Si-Si-1>=1。
1 tot = 0; 2 _Clr(head, -1); 3 for(int i=1; i<=n; i++) 4 { 5 scanf("%d", &tree[i].h); 6 tree[i].id = i; 7 } 8 sort(tree+1, tree+n+1); 9 for(int i=1; i<n; i++) 10 { 11 int u = tree[i].id; 12 int v = tree[i+1].id; 13 if(u > v) // 从左往右跳 14 Add_edge(v, u, d); 15 else // 反之。 16 Add_edge(u, v, d); 17 Add_edge(i+1, i, -1); // 相邻两课树不能在同一个位置。 18 } 19 int st=tree[1].id, ed=tree[n].id; // 找到最矮的树和最高的树的位置