zoukankan      html  css  js  c++  java
  • 差分约束

    http://972169909-qq-com.iteye.com/blog/1185527

    第一: 
    感觉难点在于建图 
    第二: 
    ①:对于差分不等式,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; 
    其他关系就要去题目探索了 

    回到上面那些题目: 

    第一题:【POJ 1201/ZOJ 1508/HDU 1384 Intervals】 
    http://poj.org/problem?id=1201 
    题意:求符合题意的最小集合的元素个数 
    设x[i]是{i}这个集合跟所求未知集合的交集元素个数,明显最大只能是1 
    再设s[i] = x[0] + x[1] + …… + x[i] 
    明显的,s[i]表示集合{0,1,2,3,……,i}与所求未知集合的交集元素个数 
    那么就有x[i] = s[i] - s[i-1] 
    ∵0 <= x[i] <= 1 
    ∴0 <= s[i] - s[i-1] <= 1 
    由于题目求最小值,所以是最长路,用的是a - b >= c这种形式 
    即有:①s[i] - s[i-1] >= 0; ②s[i-1] - s[i] >= -1; 
    按照题目输入a, b, c: 
    表示{a,a+1,a+2,……,b}(设这个集合是Q)与所求未知集合的交集元素个数至少为c 
    而s[a-1]表示{1,2,3,……,a-1}与所求未知集合的交集元素个数 
    s[b]表示{1,2,3,……,a-1,a,a+1,a+2,……,b}与所求未知集合的交集元素个数 
    ∴Q = s[b] - s[a-1]; 
    即可建立关系: ③s[b] - s[a-1] >= c; 
    但是还有一个问题a >= 0,那么a-1有可能不合法 
    解决方法:所有元素+1就可以了 
    实现:把③变成 s[b+1] - s[a] >= c; 

    第二题:【POJ 1275/ZOJ 1420/HDU 1529 Cashier Employment】 
    http://poj.org/problem?id=1275 
    文章最后有附上这题的代码。 
    这题是这里面最难的一题,建图难,而且难以找出所有约束条件 
    先说明一下,这里我把编号设定为1-24,而不是题目的0-23,并且设0为虚点 
    设x[i]是实际雇用的i时刻开始工作的员工数 
    R[i]是题目需要的i时刻正在工作的最少员工数 
    注意了,i时刻开始工作跟i时刻正在工作是完全不同的 
    设s[i] = x[1] + x[2] + …… + x[i] 
    则s[i]表示1-i这段时间开始工作的员工数 
    再设num[i]表示i时刻开始工作的最多可以雇用的员工数 
    ∴有0 <= x[i] <= num[i] 
    即 0 <= s[i] - s[i-1] <= num[i] 
    由于是求最小值,所以用最长路 
    则有:①s[i] - s[i-1] >= 0;②s[i-1] - s[i] >= -num[i]; 
    (1 <= i <= 24,虽然0是虚点,但是s[1] - s[0]也是必要的!因为x[1]也是有范围的!) 
    由于员工可以持续工作8个小时(R[i]是i时刻正在工作的最少人数) 
    ∴x[i-7] + x[i-6] + …… + x[i] >= R[i]【i-7开始工作的人在i时刻也在工作,其他同理】 
    即:③s[i] - s[i-8] >= R[i] (8 <= i <= 24) 
    但是有个特殊情况,就是从夜晚到凌晨的一段8小时工作时间 
    (x[i+17] + …… + x[24]) + (x[1] + x[2] + …… + x[i]) >= R[i]; 
    则:s[24] - s[i+16] + s[i] >= R[i]; 
    整理一下:④s[i] - s[i+16] >= R[i] - s[24]; 
    (1 <= i < 8,注意i=0是没有意义的,因为R[0]没有意义) 
    由于s[24]就是全天实际雇用的人数,而一共有n个员工可以雇用 
    所以设ans = s[24] 
    则:⑤s[i] - s[i+16] >= R[i] - ans;( 1 <= i < 8 ) 
    所以就可以从小到大暴力枚举ans【或二分枚举】,通过spfa检验是否有解即可【存在负环无解】
    但是还有一个问题,起点在哪里…… 
    这时候虚点0就起作用了,我称它为超级起点 
    于是建图后直接判断spfa(0)是否有解就可以了 
    PS:还有另外一个条件必须用到……:⑥s[24] - s[0] >= ans] 
    不用这个条件二分枚举ans可以AC,但这只是数据问题,暴力从小到大枚举木有这条件就会错,所以说这个条件最关键而又难找……要仔细找特殊点和虚点的约束关系 

    第三题:【POJ 1364/ZOJ 1260/HDU 1531 King】 
    http://poj.org/problem?id=1364 
    设s[i] = a[1] + a[2] + …… + a[i]; 
    a[Si] + a[Si+1] + ... + a[Si+ni] = s[Si+ni] - s[Si-1]; 
    所以由题意有: 
    ①s[Si+ni] - s[Si-1] > ki 
    或②s[Si+ni] - s[Si-1] < ki 
    由于只是检验是否有解,所以最短路或最长路都可用,以下是最短路要建立的关系: 
    把①化为:s[si-1] - s[si+ni] <= - ki - 1 
    把②化为:s[si+ni] - s[si-1] <= ki - 1 

    第四题:【ZOJ 1455/HDU 1534 Schedule Problem】 
    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1455 
    设第i项工作持续时间为v[i],开始时间s[i],那么结束时间就是s[i]+v[i] 
    SAS: s[a] >= s[b] ---> s[a] - s[b] >= 0 
    FAS: s[a] + v[a] >= s[b] ---> s[a] - s[b] >= -v[a] 
    SAF: s[a] >= s[b] + v[b] ---> s[a] - s[b] >= v[b] 
    FAF: s[a] + v[a] >= s[b] + v[b] ---> s[a] - s[b] >= v[b] - v[a] 
    直接最长路建图就可以了 

    第五题:【HDU 3440 House Man】 
    http://acm.hdu.edu.cn/showproblem.php?pid=3440 
    此题难度仅次于第二题,需要深刻理解差分约束 
    题意:按照序号从左往右放置房子,求最后从低到高跳到所有房子的【最大总水平路程】 
    设s(b) - s(a) <= K(一个常数)表示【设a,b为序号】: 
    b到a的距离 <= K,但是必须定一个规则,a在左边还是b在左边? 
    这里设a,b是x轴上的点,再设b > a 
    所以这样的情况下规则就是:【s(序号大的) - s(序号小的)】才表示:【b到a之间的距离】这个意义 
    当然可以设另一种规则---【序号小-序号大】表示距离---,总之要一致! 
    按照第一种规则有以下关系: 
    ①位置相邻: 
    s(i) - s(i-1) >= 1 ---> s(i-1) - s(i) <= -1 
    ②高度相邻(排序后):【num表示a[i]这间房子的序号】 
    s(max(a[i].num,a[i-1].num))-s(min(a[i].num,a[i-1].num)) <= D 
    建图后只要这样: 
    spfa (min (a[1].num, a[n].num)); 
    printf ("%d ", dist[max (a[n].num, a[1].num)]); 
    答案就出来了 
    另外推荐这种方法!比较简单:http://hi.baidu.com/tju_ant/blog/item/f22fe6d92809033833fa1c08.html 

    第六题:【HDU 3592 World Exhibition】 
    http://acm.hdu.edu.cn/showproblem.php?pid=3592 
    按照题目明显条件建立关系式立刻水过 
    但是我觉得它数据很水…… 
    题目说:Assume that there are N (2 <= N <= 1,000) people numbered 1..N who are standing in the same order as they are numbered 
    也就是说人是按序号排的啊 
    那应该还有一个约束条件才对吧:s(i) - s(i-1) >= 0; 
    就像这组数据: 

    3 2 1 
    1 2 1 
    1 3 2 
    2 3 3 
    要按序号排队不可能满足,应该输出-1 
    总之要不要这个约束条件都能AC……我特么的彻底无语了 


    第七题:【HDU 3666 THE MATRIX PROBLEM】 
    http://acm.hdu.edu.cn/showproblem.php?pid=3666 
    这题spfa用队列卡数据容易超时,用栈吧…… 
    由题意得对于矩阵每个元素【设为w,处于矩阵第i行,第j列】 
    L <= w*a[i]/b[j] <= U 
    两边取对数有: 
    lg(L) <= lg(w)+lg(a[i])-lg(b[j]) <= lg(U) 
    按照最长路整理得【也可以用最短路】: 
    ①lg(a[i])-lg(b[j]) >= lg(L)-lg(w);②lg(b[j])-lg(a[i]) >= lg(w)-lg(U) 
    把a和b数组合并成一个数组s得【a有n个元素,b有m个元素,这里把b接在a数组后面】: 
    ①s(i) - s(j+n) >= lg(L) - lg(w) 
    ②s(j+n) - s(i) >= lg(w) - lg(U) 
    最后加个超级起点即可 
    我把0作为超级起点: 
    for (i = 1; i <= n + m; i++) 
        addedge (0, i, 0); 
    spfa (0); 


    最后弱弱的献上第二题代码: 

    C++代码  收藏代码
      1. #include <iostream>  
      2. #include <queue>  
      3. using namespace std;  
      4. #define inf 0x7fffffff  
      5. #define M 25  
      6.   
      7. struct edge{  
      8.     int v, w, next;  
      9. }e[5*M];  
      10. bool inq[M];  
      11. int dist[M], pre[M], n = 24, cnt, ind[M];  
      12.   
      13. void init ()  
      14. {  
      15.     memset (pre, -1, sizeof(pre)); cnt = 0;  
      16. }  
      17. void addedge (int u, int v, int w)  
      18. {  
      19.     e[cnt].v = v; e[cnt].w = w; e[cnt].next = pre[u]; pre[u] = cnt++;  
      20. }  
      21. bool spfa (int u)  
      22. {  
      23.     int i, v, w;  
      24.     for (i = 0; i <= n; i++)  
      25.     {  
      26.         dist[i] = -inf;  
      27.         inq[i] = false;  
      28.         ind[i] = 0;  
      29.     }  
      30.     queue<int> q;  
      31.     q.push (u);  
      32.     inq[u] = true;  
      33.     dist[u] = 0;  
      34.     while (!q.empty())  
      35.     {  
      36.         u = q.front();  
      37.         if (++ind[u] > n)  
      38.             return false;  
      39.         q.pop();  
      40.         inq[u] = false;  
      41.         for (i = pre[u]; i != -1; i = e[i].next)  
      42.         {  
      43.             v = e[i].v;  
      44.             w = e[i].w;  
      45.             if (dist[u] + w > dist[v])  
      46.             {  
      47.                 dist[v] = dist[u] + w;  
      48.                 if (!inq[v])  
      49.                 {  
      50.                     q.push (v);  
      51.                     inq[v] = true;  
      52.                 }  
      53.             }  
      54.         }  
      55.     }  
      56.     return true;  
      57. }  
      58.   
      59. int main()  
      60. {  
      61.     int t, R[M], num[M], i, pos, m, l, r, ans;  
      62.     scanf ("%d", &t);  
      63.     while (t--)  
      64.     {  
      65.         for (i = 1; i <= n; i++)  
      66.             scanf ("%d", R+i);  
      67.         scanf ("%d", &m);  
      68.         memset (num, 0, sizeof(num));  
      69.         for (i = 0; i < m; i++)  
      70.         {  
      71.             scanf ("%d", &pos);  
      72.             num[pos+1]++;  
      73.         }  
      74.         l = 0, r = m;  
      75.         bool flag = true;  
      76.         while (l < r)        //二分枚举ans  
      77.         {  
      78.             init();  
      79.             ans = (l+r) / 2;  
      80.             for (i = 1; i <= n; i++)  
      81.             {  
      82.                 addedge (i-1, i, 0);            //s[i] - s[i-1] >= 0  
      83.                 addedge (i, i-1, -num[i]);      //s[i-1] - s[i] >= -num[i]  
      84.             }  
      85.             for (i = 8; i <= n; i++)  
      86.                 addedge (i-8, i, R[i]);         //s[i] - s[i-8] >= R[i]  
      87.             for (i = 1; i < 8; i++)  
      88.                 addedge (i+16, i, R[i]-ans);    //s[i] - s[i+16] >= R[i] - ans  
      89.             addedge (0, 24, ans);               //s[24] - s[0] >= ans  
      90.             if (spfa (0))  
      91.                 r = ans, flag = false;  
      92.             else l = ans + 1;  
      93.         }  
      94.         if (flag)  
      95.             puts ("No Solution");  
      96.         else printf ("%d ", r);  
      97.     }  
      98.     return 0;  
      99. }  
  • 相关阅读:
    跑路了
    *CTF 2019 quicksort、babyshell、upxofcpp
    pyspark如何遍历broadcast
    pwn易忘操作原理笔记
    pwn学习之四
    pwn学习之三
    pwn学习之二
    pwn学习之一
    2017GCTF部分writeup
    OD使用教程12
  • 原文地址:https://www.cnblogs.com/Opaser/p/4345767.html
Copyright © 2011-2022 走看看