• 差分约束系统小结


    百科是这样说的:如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法

      学习差分约束系统之前,必须掌握单源最短路径的算法(Bellman-ford、SPFA)。习惯上用SPFA解差分约束的题目(其实SPFA是由Bellman-ford优化而来,看代码可以知道Bellman-ford的dist[v]需要松弛n-1次,SPFA在发现dist[v]没有变化后就不再进行松弛了

          

    解题方法有两种:

      假设j > i,

      1、由xj - xi >=w得到xj >= xi + w,建立一条从xi->xj的边权为w的边。然后从小到大求最长路算法。

      2、由xj - xi >=w得到xi <= xj - w,建立一条从xj->xi的边权为-w的边。然后从大到小求最短路算法。

      实际上,最长路和最短路算法的区别很小(SPFA模版)。

        最长路算法中dist[]初始化为负无穷,松弛的时候是if(dist[v] < dist[u] + Map[u][v]) 。

        最短路算法中dist[]初始化为正无穷,松弛的时候是if(dist[v] > dist[u] + Map[u][v]) 。

    为了方便记忆以及做题,就以最短路模型来详细讲解:

      对于每一个未知量xi,都可以看作是图中的一个顶点。每一个不等式看作图中的一条有向边。对于每一个差分约束xj - xi <=bk(记住是小于等于),建立边<x1,xj>,权值为bk。用小于等于来建边是为了可以用最短路的方法来解。可以这样理解,xi,xj是X轴正向上的点,bk都是正的值,如果知道了xi的值,那么从约束条件 xj - xi <=bk可以知道xj的上限,求xj的最小值其实就是求xi到xj的最短距离。这样能理解,就有思路了。这样建边还是不够的,我们并不能保证图是连通的,可以建立一个超级源点s,s到每个点都有一条权值为0边。当然,有些题目并没有这么简单,有一些隐含条件,需要用来建边。

      需要注意的是,图中存在负环,则该差分约束就无解。当s到vi的最短距离为INF的时候,说明该点对应的变量可以任意取值。

    下面是具体的题目:

      hdu1384 

      题目大意是:

        给出一些区间[ai,bi]和每个区间最少需要ci个点,然后问总共最少需要几个点满足所有区间的要求。

      由于1<=ai<=bi,所以ai-1或者bi+1都是没关系的,都是可以的。(下标从0开始和1开始都行,却不能是负数)

      设s[i]是1~i 中需要的点的个数。则s[j+1]-s[i]>=w。这只是题目给出的数据所能建的图。为什么是j+1而不是j,闭区间[i,j]之间有j-i+1个整数点,我们建图也需要在他们之间有那么多个点。

      但是有很多点是没有连通的。根据隐含条件 0=<s[i+1] - s[i] <=1,可以对每个点都再建两条边。

      这样就可以做题了。

      如果选择建立xj >= xi + w的关系来建图,就需要用最长路经来解题。dist[]初始化为负无穷。

      PS:这题不能用建立超级源点的方法来做。

      具体的看代码: 

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<queue>
     5 #include<algorithm>
     6 using namespace std;
     7 
     8 const int N = 50010, M=150010;
     9 const int INF = 0x3f3f3f3f;
    10 struct node
    11 {
    12     int to, w, next;
    13 };
    14 node edge[M];
    15 int head[N], dist[N], outq[N];
    16 bool vis[N];
    17 int tot;
    18 bool SPFA(int s, int n )
    19 {
    20     int i,k;
    21     for(i=0;i<=n;i++) dist[i]=-INF;
    22     memset(vis,0,sizeof(vis));
    23     memset(outq,0,sizeof(outq));
    24     queue<int > q;
    25     while(!q.empty()) q.pop();
    26     vis[s]=1;
    27     dist[s]=0;
    28     q.push(s);
    29     while(!q.empty())
    30     {
    31         int u=q.front();
    32         q.pop();
    33         vis[u]=0;
    34         outq[u]++;
    35         if(outq[u]>n) return 0 ;
    36         k=head[u];
    37         while(k>=0)
    38         {
    39             if(dist[edge[k].to]-edge[k].w<dist[u])
    40             {
    41                 dist[edge[k].to]=dist[u]+edge[k].w;
    42                 if(!vis[edge[k].to])
    43                 {
    44                     vis[edge[k].to]=1;
    45                     q.push(edge[k].to);
    46                 }
    47             }
    48             k=edge[k].next;
    49         }
    50     }
    51     return 1;
    52 }
    53 void addedge(int i, int j ,int w)
    54 {
    55     edge[tot].to=j;
    56     edge[tot].w=w;
    57     edge[tot].next=head[i];
    58     head[i]=tot++;
    59 }
    60 void init()
    61 {
    62     memset(head,-1,sizeof(head));
    63     tot=0;
    64 }
    65 int main()
    66 {
    67     //freopen("test.txt","r",stdin);
    68     int n,m,i,j,k,w,a,b;
    69     while(scanf("%d",&m)!=EOF)
    70     {
    71         init();
    72         a=INF,b=-INF;
    73         for(k=0;k<m;k++)
    74         {
    75             scanf("%d %d %d",&i,&j,&w);
    76             if(a>i) a=i;
    77             if(b<j+1) b=j+1;
    78             addedge(i,j+1,w);
    79         }
    80         for(i=a;i<=b;i++)
    81         {
    82             addedge(i,i+1,0);
    83             addedge(i+1,i,-1);
    84         }
    85         if(SPFA(a,b)) printf("%d
    ",dist[b]);
    86     }
    87     return 0;
    88 }
    View Code

       hdu 1534  Schedule Problem

      题目大意:

        某一任务的分为n个部分,给出完成每一部分所需要的时间以及某些部分任务之间的四种约束。求每个部分的开始的时间。  

      思路:  

        以求最长路的方法来做。

        设p[i]是完成i所需要的时间,这是已知的。设s[i]为任务i开始的时间。

        SAS   addedge(j, i, 0);      可以表示为s[i]>=s[j] 。任务i在任务j开始以后才开始。
        SAF   addedge(j, i, p[j]);        可以表示为s[i]>=s[j] + p[j] 。任务i在任务j完成以后才开始。 
        FAS   addedge(j, i, -p[i]);     可以表示为s[i]>=s[j] - p[i] 。任务i在任务j开始以后才完成。
        FAF   addedge(j, i ,p[j]-p[i]);      可以表示为s[i]>=s[j] +p[j] - p[i] 。任务i在任务j完成以后才完成。

      这样,题目给出的条件已经建边了。但是图可能不连通,所以需要建立超级源点。比如说,输入为SAS 1 2  SAF 3 4 。 以1为源点的话图是不连通的。为什么超级源点是合理的呢?因为1和3是可以同时开始的,他们并没有约束。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<queue>
     5 #include<algorithm>
     6 using namespace std;
     7 
     8 const int N = 50010, M=150010;
     9 const int INF = 0x3f3f3f3f;
    10 struct node
    11 {
    12     int to, w, next;
    13 };
    14 node edge[M];
    15 int head[N], dist[N], outq[N];
    16 bool vis[N];
    17 int tot;
    18 bool SPFA(int s, int n )
    19 {
    20     int i,k;
    21     for(i=0;i<=n;i++) dist[i]=-INF;
    22     memset(vis,0,sizeof(vis));
    23     memset(outq,0,sizeof(outq));
    24     queue<int > q;
    25     while(!q.empty()) q.pop();
    26     vis[s]=1;
    27     dist[s]=0;
    28     q.push(s);
    29     while(!q.empty())
    30     {
    31         int u=q.front();
    32         q.pop();
    33         vis[u]=0;
    34         outq[u]++;
    35         if(outq[u]>n) return 0 ;
    36         k=head[u];
    37         while(k>=0)
    38         {
    39             if(dist[edge[k].to]-edge[k].w<dist[u])
    40             {
    41                 dist[edge[k].to]=dist[u]+edge[k].w;
    42                 if(!vis[edge[k].to])
    43                 {
    44                     vis[edge[k].to]=1;
    45                     q.push(edge[k].to);
    46                 }
    47             }
    48             k=edge[k].next;
    49         }
    50     }
    51     return 1;
    52 }
    53 void addedge(int i, int j ,int w)
    54 {
    55     edge[tot].to=j;
    56     edge[tot].w=w;
    57     edge[tot].next=head[i];
    58     head[i]=tot++;
    59 }
    60 void init()
    61 {
    62     memset(head,-1,sizeof(head));
    63     tot=0;
    64 }
    65 int main()
    66 {
    67     //freopen("test.txt","r",stdin);
    68     int n,m,i,j,k,w,a,b;
    69     while(scanf("%d",&m)!=EOF)
    70     {
    71         init();
    72         a=INF,b=-INF;
    73         for(k=0;k<m;k++)
    74         {
    75             scanf("%d %d %d",&i,&j,&w);
    76             if(a>i) a=i;
    77             if(b<j+1) b=j+1;
    78             addedge(i,j+1,w);
    79         }
    80         for(i=a;i<=b;i++)
    81         {
    82             addedge(i,i+1,0);
    83             addedge(i+1,i,-1);
    84         }
    85         if(SPFA(a,b)) printf("%d
    ",dist[b]);
    86     }
    87     return 0;
    88 }
    View Code

      hdu3440 House Man

      题目大意:(我自己说不清楚,拷贝自 http://www.cnblogs.com/scau20110726/archive/2013/05/04/3059625.html

        有n个屋子,超人从最矮的屋子开始,依次跳下比当前屋子高且最接近当前高度的屋子(即按照屋子高度增序来跳),但超人跳跃还有一个水平距离限制D,  他每次跳的水平距离<=D。现在给你每个屋子的高度是它们的相对位置,你不能改变屋子的相对位置,但是可以水平移动屋子,使得最矮的屋子和最高的屋子的水平距离最大。如果无论怎样移动,超人都无法跳到最后那个屋子则输出-1。

      思路和建图:

        建立一个结构体,存放房子的高度和标号。按照房子高度排序。建图规则,任意高度(直接)相邻的房子之间的距离满足 1<=dist[i][j]<=D 。

      注意:建图的规则不能错。如果把相邻两点理解为任意两点之间的距离就错了。(惭愧,我就这样错了!)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<queue>
     4 #include<cstring>
     5 #include<algorithm>
     6 using namespace std;
     7 const int N = 1010, M=10010;
     8 const int INF = 0x3f3f3f3f;
     9 struct node
    10 {
    11     int to, w, next;
    12 };
    13 node edge[M];
    14 int head[N], dist[N], outq[N];
    15 bool vis[N];
    16 int tot,D;
    17 bool SPFA(int s, int n )
    18 {
    19     int i,k;
    20     for(i=0;i<=n;i++) dist[i]=INF;
    21     memset(vis,0,sizeof(vis));
    22     memset(outq,0,sizeof(outq));
    23     queue<int > q;
    24     while(!q.empty()) q.pop();
    25     vis[s]=1;
    26     dist[s]=0;
    27     q.push(s);
    28     while(!q.empty())
    29     {
    30         int u=q.front();
    31         q.pop();
    32         vis[u]=0;
    33         outq[u]++;
    34         if(outq[u]>n) return 0 ;
    35         k=head[u];
    36         while(k>=0)
    37         {
    38             if(dist[edge[k].to]-edge[k].w>dist[u])
    39             {
    40                 dist[edge[k].to]=dist[u]+edge[k].w;
    41                 if(!vis[edge[k].to])
    42                 {
    43                     vis[edge[k].to]=1;
    44                     q.push(edge[k].to);
    45                 }
    46             }
    47             k=edge[k].next;
    48         }
    49     }
    50     return 1;
    51 }
    52 void addedge(int i,int j,int w)
    53 {
    54     edge[tot].to=j;
    55     edge[tot].w=w;
    56     edge[tot].next=head[i];
    57     head[i]=tot++;
    58 }
    59 void init()
    60 {
    61     tot=0;
    62     memset(head,-1,sizeof(head));
    63 }
    64 struct house
    65 {
    66     int id,d;
    67 }p[N];
    68 bool cmp(const house &a, const house &b)
    69 {
    70     return a.d < b.d;
    71 }
    72 int main()
    73 {
    74     //freopen("test.txt","r",stdin);
    75     int n,i,j,k,t=0,cas;
    76     scanf("%d",&cas);
    77     while(cas--)
    78     {
    79         scanf("%d%d",&n,&D);
    80         init();
    81         for(k=0;k<n;k++)
    82         {
    83             scanf("%d",&p[k].d);
    84             p[k].id=k+1;
    85         }
    86         sort(p,p+n,cmp);
    87         for(i = 1; i < n; i ++){
    88            addedge(i+1, i, -1);
    89            addedge(min(p[i].id, p[i - 1].id), max(p[i].id, p[i - 1].id), D);
    90         }
    91         printf("Case %d: ",++t);
    92         int u=min(p[0].id,p[i-1].id),v=max(p[0].id,p[i-1].id);
    93         if(SPFA(u,n)) printf("%d
    ",dist[v]);
    94         else printf("-1
    ");
    95     }
    96     return 0;
    97 }
    View Code

       上面的一个是求最短路的代码。下面是求最大路的代码。看得懂了,基本上就会查分约束了。

      hdu 3440

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<queue>
     4 #include<cstring>
     5 #include<algorithm>
     6 using namespace std;
     7 const int N = 1010, M=10010;
     8 const int INF = 0x3f3f3f3f;
     9 struct node
    10 {
    11     int to, w, next;
    12 };
    13 node edge[M];
    14 int head[N], dist[N], outq[N];
    15 bool vis[N];
    16 int tot,D;
    17 bool SPFA(int s, int n )
    18 {
    19     int i,k;
    20     for(i=0;i<=n;i++) dist[i]=-INF;
    21     memset(vis,0,sizeof(vis));
    22     memset(outq,0,sizeof(outq));
    23     queue<int > q;
    24     while(!q.empty()) q.pop();
    25     vis[s]=1;
    26     dist[s]=0;
    27     q.push(s);
    28     while(!q.empty())
    29     {
    30         int u=q.front();
    31         q.pop();
    32         vis[u]=0;
    33         outq[u]++;
    34         if(outq[u]>n) return 0 ;
    35         k=head[u];
    36         while(k>=0)
    37         {
    38             if(dist[edge[k].to]-edge[k].w<dist[u])
    39             {
    40                 dist[edge[k].to]=dist[u]+edge[k].w;
    41                 if(!vis[edge[k].to])
    42                 {
    43                     vis[edge[k].to]=1;
    44                     q.push(edge[k].to);
    45                 }
    46             }
    47             k=edge[k].next;
    48         }
    49     }
    50     return 1;
    51 }
    52 void addedge(int i,int j,int w)
    53 {
    54     edge[tot].to=j;
    55     edge[tot].w=w;
    56     edge[tot].next=head[i];
    57     head[i]=tot++;
    58 }
    59 void init()
    60 {
    61     tot=0;
    62     memset(head,-1,sizeof(head));
    63 }
    64 struct house
    65 {
    66     int id,d;
    67 }p[N];
    68 bool cmp(const house &a, const house &b)
    69 {
    70     return a.d < b.d;
    71 }
    72 int main()
    73 {
    74     //freopen("test.txt","r",stdin);
    75     int n,i,j,k,t=0,cas;
    76     scanf("%d",&cas);
    77     while(cas--)
    78     {
    79         scanf("%d%d",&n,&D);
    80         init();
    81         for(k=0;k<n;k++)
    82         {
    83             scanf("%d",&p[k].d);
    84             p[k].id=k+1;
    85         }
    86         sort(p,p+n,cmp);
    87         for(i = 1; i < n; i ++){
    88            addedge(i, i+1, 1);
    89            addedge(max(p[i].id, p[i - 1].id), min(p[i].id, p[i - 1].id), -D);
    90         }
    91         printf("Case %d: ",++t);
    92         int u=max(p[0].id,p[i-1].id),v=min(p[0].id,p[i-1].id);
    93         if(SPFA(u,n)) printf("%d
    ",-dist[v]);
    94         else printf("-1
    ");
    95     }
    96     return 0;
    97 }
    View Code

     

  • 相关阅读:
    Unable to satisfy the following requirements解决方式
    零基础学python》(第二版)
    mysql 更新数据表的记录
    mysql创建数据库和删除数据库
    正则表达式
    python lambda函数详细解析(面试经常遇到)
    Linux 命令 统计进程数目
    Python 时间戳与时间字符串互相转
    python 安装配置(windows)
    linux 系统 tar 的用法详解
  • 原文地址:https://www.cnblogs.com/Potato-lover/p/3959979.html
Copyright © 2020-2023  润新知