zoukankan      html  css  js  c++  java
  • 初涉差分约束系统

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

    差分约束系统

    差分约束系统的定义是:一个由$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
    ",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
    ",cnt);
    83     return 0;
    84 }

    【未知条件约束】poj1275Cashier Employment

    题目链接:http://poj.org/problem?id=1275

    现在还不会……

    END

  • 相关阅读:
    file_put_contents 和php://input 实现存储数据进图片中
    Oracle PL/SQL游标的学习
    三层的再理解
    取出字符串中任意的顺序匹配
    oracle的正则表达式(regular expression)简单介绍
    oracle动态游标的简单实现方法
    存储过程之游标笔记小结
    char,varchar,nvarchar的区别
    大连惊魂记
    让asp.net默认的上传组件支持进度条反映(转)
  • 原文地址:https://www.cnblogs.com/antiquality/p/9047927.html
Copyright © 2011-2022 走看看