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

    差分约束

      差分约束是一种非常神奇的算法,其实也可以说是一种最短路的建模技巧。这个算法其实学过一次,但是又忘掉了...所以这次记一记。

      比如给出一些不等式:$a_i-a_j<=c$,要求判断是否有解,或者是求最小的解。第一次见到这种题的时候感觉无从下手,而且洛谷上还标着“图论”的标签。所以说是数形结合。让我们把这个式子化一化:

      进行移项,成为$a_i<=a_j+c$,令$c=w(j,i)$,就得到:$d[i]<=d[j]+w(j,i)$,非常像最短路的松弛。如果式子出现变化,通过一些移项,取相反数化为一般的样子就可以啦。注意如果两个变量相等要连双向边。接下来的做法就取决于题目了,下面说说我见过的几种:

      1.SPFA找负环判断是否有解;

      2.图有可能会不连通,这时候虚构一个源点向所有其他点连一些边,具体怎么连看题目要求;

      3.求解的数目...刚刚发现这种情况是不存在的,因为差分约束要么无解,要么有无数组解(已有的解全部加上一个常数);

      4.最短路:求$a_n-a_1$的最大值,这里看起来非常不符合常理,但是是正确的。最短路的松弛操作是这样的:

     1 if(d[beg]+g[i].co>d[j])
      2     d[j]=d[beg]+g[i].co;

      事实上最短路的解也就是要求对于所有的beg,j达到d[beg]+g[i].co<=d[j]的效果,所以说最短路更新出来的就是差分约束的最大值;

      5.最长路:和4的道理差不多;

       

      真是令人非常开心啊,现在可以看题了。

       小K的农场:https://www.luogu.org/problemnew/show/P1993

      写了这个blog之后对差分约束更明白了,这题就是个板子题,只需要判断是否有解就可以了。写这道题的时候建图思路很清奇啊,和上面讲的完全不是一个写法。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <cstring>
     4 # include <queue>
     5 
     6 using namespace std;
     7 
     8 struct edge
     9 {
    10     int nex,too,co;
    11 }g[10009];
    12 
    13 int a,b,c,n,h=0,m,op,x,y,k,firs[10007],d[10007],s[10007];
    14 bool f=false,vis[10007],in_q[10009]={false};
    15 queue <int> q;
    16 
    17 void add(int x,int y,int co)
    18 {
    19     g[++h].co=co;
    20     g[h].too=y;
    21     g[h].nex=firs[x];
    22     firs[x]=h;
    23 }
    24 
    25 void dfs(int x)
    26 {
    27     int j;
    28     vis[x]=true;
    29     for (int i=firs[x];i;i=g[i].nex)
    30     {
    31         j=g[i].too;
    32         if(d[j]>d[x]+g[i].co)
    33         {
    34             if(vis[j])
    35             {
    36                 f=true;
    37                 return ;
    38             }
    39             d[j]=d[x]+g[i].co;
    40             dfs(j);
    41         }
    42     }
    43     vis[x]=false;
    44     return ;
    45 }
    46 int main()
    47 {
    48     scanf("%d%d",&n,&m);
    49     for (int i=1;i<=m;i++)
    50     {
    51         scanf("%d%d%d",&op,&a,&b);
    52         if(op==1)
    53         {
    54             scanf("%d",&c);
    55             add(a,b,-1*c);
    56         }
    57         else if(op==2)
    58         {
    59             scanf("%d",&c);
    60             add(b,a,c);
    61         }
    62         else if(op==3)
    63         {
    64             add(a,b,0);
    65             add(b,a,0);
    66         }
    67     }
    68     for (int i=1;i<=n;i++)
    69     {
    70         d[i]=0;
    71         dfs(i);
    72         if(f) break;
    73     }
    74     if(f)  printf("No"); else printf("Yes");
    75     return 0;
    76 }
    小K的农场

       

      [SCOI2011]糖果:https://www.luogu.org/problemnew/show/P3275

       这题堪称大杂烩,有五种操作,思维要清晰一点,多写写画画。因为要求分到的糖最少,所以求最长路,注意每个小朋友都必须有糖吃才行。

      
     1 // luogu-judger-enable-o2
     2 # include <cstdio>
     3 # include <iostream>
     4 # include <queue>
     5 # include <cstring>
     6 # define R register int
     7 
     8 int n,k,firs[100009],h,a,b,x,d[100009],t[100009];
     9 bool vis[100009];
    10 std::queue <int> q;
    11 long long ans=0;
    12 struct edge
    13 {
    14     int too,co,nex;
    15 }g[2000009];
    16 
    17 int read()
    18 {
    19     int x=0;
    20     char c=getchar();
    21     while (!isdigit(c))
    22         c=getchar();
    23     while (isdigit(c))
    24         x=(x<<3)+(x<<1)+(c^48),c=getchar();
    25     return x;    
    26 }
    27 
    28 void add (int x,int y,int co)
    29 {
    30     g[++h].co=co;
    31     g[h].too=y;
    32     g[h].nex=firs[x];
    33     firs[x]=h;
    34 }
    35 
    36 int main()
    37 {
    38     n=read();
    39     k=read();
    40     for (R i=1;i<=k;++i)
    41     {
    42         x=read(),a=read(),b=read();
    43         if(x==1) add(a,b,0),add(b,a,0);
    44         else if(x==2) add(a,b,1);
    45         else if(x==3) add(b,a,0);
    46         else if(x==4) add(b,a,1);
    47         else if(x==5) add(a,b,0);
    48         if(a==b&&(x==2||x==4))
    49         {
    50             printf("-1");
    51             return 0;
    52         }
    53     }
    54     for (R i=n;i>=1;--i)
    55         add(0,i,1);
    56     vis[0]=true;
    57     q.push(0);
    58     int beg,j;
    59     while (q.size())
    60     {
    61         beg=q.front();
    62         q.pop();
    63         vis[beg]=false;
    64         if(t[beg]==n-1) 
    65         {
    66             printf("-1");
    67             return 0;
    68         }
    69         t[beg]++;
    70         for (R i=firs[beg];i;i=g[i].nex)
    71         {
    72             j=g[i].too;
    73             if(d[beg]+g[i].co>d[j])
    74             {
    75                 d[j]=d[beg]+g[i].co;
    76                 if(vis[j]==false) vis[j]=true,q.push(j);
    77             }
    78         }
    79     }
    80     for (int i=1;i<=n;++i)
    81         ans+=d[i];
    82     printf("%lld",ans);
    83     return 0;
    84 }
    糖果

     

      学算法还是重在理解啊,之前没理解的时候只能靠举例子来猜是最长路还是最短路,是从a到b连边还是反过来,现在就不用纠结这些了。

      [HNOI2005]狡猾的商人:https://www.luogu.org/problemnew/show/P2294

      题意概述:给出一些关于区间和的信息,判断是否有冲突。

      区间和看起来不是很好维护,但是不要忘了还有前缀和这种奇妙的东西,如果说$a[i]+a[i+1]...+a[j]=c$,可以化为这个样子:$s[j]-s[i-1]=c$,再拆成两个不等式连边,$s[t]<=s[s-1]+w,s[s-1]<=s[t]-w$,最后SPFA找负环判断可行性。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <queue>
     4 # include <cstring>
     5 # define R register int
     6 
     7 int S,T,v,w,n,m,h,firs[109],vis[109],t[109],d[109];
     8 struct edge
     9 {
    10     int too,nex,co;
    11 }g[100009];
    12 std::queue <int> q;
    13 bool f;
    14 
    15 void add (int x,int y,int co)
    16 {
    17     g[++h].too=y;
    18     g[h].nex=firs[x];
    19     firs[x]=h;
    20     g[h].co=co;
    21 }
    22 
    23 int main()
    24 {
    25     scanf("%d",&w);
    26     while (w--)
    27     {
    28         f=true;
    29         memset(d,0,sizeof(d));
    30         memset(vis,0,sizeof(vis));
    31         memset(t,0,sizeof(t));
    32         memset(firs,0,sizeof(firs));
    33         h=0;
    34         scanf("%d%d",&n,&m);
    35         for (R i=1;i<=m;++i)
    36         {
    37             scanf("%d%d%d",&S,&T,&v);
    38             add(T,S-1,v);
    39             add(S-1,T,-v);
    40         }
    41         for (R i=1;i<=n;++i)
    42             add(n+1,i,1);
    43         while (q.size()) q.pop();
    44         q.push(n+1);
    45         vis[n+1]=true;
    46         int beg,j;
    47         while (q.size())
    48         {
    49             beg=q.front();
    50             q.pop();
    51             vis[beg]=false;
    52             if(t[beg]==n-1) 
    53             {
    54                 f=false;
    55                 break;
    56             }
    57             t[beg]++;
    58             for (R i=firs[beg];i;i=g[i].nex)
    59             {
    60                 j=g[i].too;
    61                 if(d[beg]+g[i].co>d[j])
    62                 {
    63                     d[j]=d[beg]+g[i].co;
    64                     if(vis[j]==false) vis[j]=true,q.push(j);
    65                 }
    66             }
    67         }
    68         if(f) printf("true
    ");
    69         else printf("false
    ");
    70     }
    71     return 0;
    72 }
    狡猾的商人

     


     

      bzoj1731 layout:https://www.lydsy.com/JudgeOnline/problem.php?id=1731

       题意概述:数轴上有一排数,给出一些不等式,求$a_n-a_1$的最大值。

      这道题本身不是很难,但是有一些思想是比较重要的。题目里面往往隐含了一些条件,不要忘记把他们加进差分约束系统中去。这道题提到数字是按照不降序排列的,所以可以得到$a_{i}-a_{i-1}>=0$这样的形式。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <cstring>
     4 # include <queue>
     5 # define R register int
     6 # define inf 1000000009
     7 
     8 int a,b,n,h,ml,md,firs[1009],t[1009];
     9 long long co,d[1009];
    10 std::queue <int> q;
    11 bool vis[1009],inq[1009];
    12 struct edge
    13 {
    14     int too,nex;
    15     long long co;
    16 }g[30009];
    17 
    18 void add (int x,int y,long long co)
    19 {
    20     g[++h].co=co;
    21     g[h].too=y;
    22     g[h].nex=firs[x];
    23     firs[x]=h;
    24 }
    25 
    26 int main()
    27 {
    28     scanf("%d%d%d",&n,&ml,&md);
    29     for (R i=1;i<=ml;++i)
    30     {
    31         scanf("%d%d%lld",&a,&b,&co);
    32         add(a,b,co);
    33     }
    34     for (int i=1;i<=md;++i)
    35     {
    36         scanf("%d%d%lld",&a,&b,&co);
    37         add(b,a,-co);
    38     }
    39     for (int i=2;i<=n;++i)
    40         add(i,i-1,0);
    41     q.push(1);
    42     int j,beg;
    43     for (int i=1;i<=n;++i)
    44         d[i]=inf;
    45     d[1]=0;
    46     while (q.size())
    47     {
    48         beg=q.front();
    49         q.pop();
    50         vis[beg]=false;
    51         if(t[beg]==n-1)
    52         {
    53             printf("-1");
    54             return 0;
    55         }
    56         t[beg]++;
    57         for (int i=firs[beg];i;i=g[i].nex)
    58         {
    59             j=g[i].too;
    60             if(d[beg]+g[i].co<d[j]) 
    61             {
    62                 d[j]=d[beg]+g[i].co;
    63                 if(!vis[j]) vis[j]=true,q.push(j);
    64             }
    65         }
    66     }
    67     if(d[n]==inf) printf("-2");
    68     else printf("%lld",d[n]);
    69     return 0;
    70 }
    layout

       


      

      [USACO13OPEN]照片Photo:https://www.luogu.org/problemnew/show/P3084

      题意概述:给定m个包含于[1,n]的区间,要求每个区间有且只有一个特殊点,判断是否有解,如果有,输出最多有多少个特殊点。$1<= M <=100,000,1<= N <=200,000$

      一般这种涉及到区间的就可以考虑用前缀和搞一搞,因为每个点必然是0或1,所以前缀和隐含一个条件$0<=s[i]-s[i-1]<=1$.把前缀和按题意连一连就好了。不过这题太神了,不仅要判负环,还卡SPFA...学了一个新的技巧,用堆优化SPFA,有的时候其实还是快的,可以更快的更新出最小值来,但是因题而异。题解表示...如果快超时了就认为有负环,感觉海星。

      什么是堆优化SPFA呢?就是把堆优化dij的vis数组去掉再加上in_que数组。题解还说这题可以dp,感觉非常神仙。

      
     1 // luogu-judger-enable-o2
     2 # include <cstdio>
     3 # include <iostream>
     4 # include <queue>
     5 # include <cstring>
     6 # include <ctime>
     7 # define R register int
     8 
     9 using namespace std;
    10 
    11 const int maxn=200009;
    12 const int maxm=1000009;
    13 int n,x,h,y,m,firs[maxn],d[maxn],t[maxn];
    14 typedef pair <int,int> pii;
    15 priority_queue <pii,vector<pii>,greater<pii> > q;
    16 bool in_que[maxn],f;
    17 struct edge
    18 {
    19     int too,nex,co;    
    20 }g[maxm];
    21 
    22 int read()
    23 {
    24     int x=0;
    25     char c=getchar();
    26     while (!isdigit(c))
    27         c=getchar();
    28     while (isdigit(c))
    29     {
    30         x=(x<<3)+(x<<1)+(c^48);
    31         c=getchar();    
    32     }
    33     return x;
    34 }
    35 
    36 void add (int x,int y,int co)
    37 {
    38     g[++h].too=y;
    39     g[h].nex=firs[x];
    40     firs[x]=h;
    41     g[h].co=co;
    42 }
    43 
    44 int main()
    45 {    
    46 
    47     scanf("%d%d",&n,&m);
    48     for (R i=1;i<=m;++i)
    49     {
    50         x=read();
    51         y=read();
    52         if(x>y) swap(x,y);
    53         add(y,x-1,-1);
    54         add(x-1,y,1);
    55     }
    56     for (R i=1;i<=n;++i)
    57         add(i,i-1,0),add(i-1,i,1);
    58     memset(d,0x3f,sizeof(d));
    59     d[0]=0;
    60     q.push(make_pair(0,0));
    61     int beg,j;
    62     while (q.size())
    63     {
    64         if ((float)clock() / CLOCKS_PER_SEC > 0.9)
    65         {
    66             f=true;
    67             break;
    68         }
    69         beg=q.top().second;
    70         q.pop();
    71         in_que[beg]=false;
    72         if(t[beg]==n-1)
    73         {
    74             f=true;
    75             break;
    76         }
    77         t[beg]++;
    78         for (R i=firs[beg];i;i=g[i].nex)
    79         {
    80             j=g[i].too;
    81             if(d[j]>d[beg]+g[i].co)
    82             {
    83                 d[j]=d[beg]+g[i].co;
    84                 if(!in_que[j]) in_que[j]=true,q.push(make_pair(d[j],j)); 
    85             }
    86         }
    87     }
    88     if(f) printf("-1");
    89     else printf("%d",d[n]); 
    90     return 0;
    91 }
    照片Photo

      ---shzr

  • 相关阅读:
    Android 最简单的基于FFmpeg的移动端例子:Android HelloWorld
    实现简易的android 直播技术
    Android Service和Thread的关系
    Android Fragment 你应该知道的一切
    SQLyog普通版与SQLyog企业版对比分析
    Sqoop异常解决ERROR tool.ImportTool: Encountered IOException running import job: java.io.IOException: No columns to generate for ClassWriter问题
    SQLyog软件里无法插入中文(即由默认的latin1改成UTF8编码格式)
    Sqoop-1.4.6工具import和export使用详解(官网)
    Sqoop Export原理和详细流程讲解
    Sqoop Import原理和详细流程讲解
  • 原文地址:https://www.cnblogs.com/shzr/p/9350457.html
Copyright © 2011-2022 走看看