• 差分约束系统


     

    一、算法简介

      差分约束系统是一种特殊的N元一次不等式组,它包含N个变量,X1~Xn以及m个约束条件,每一个约束条件都是由两个变量作差构成的,形如Xi-Xj≤Ck,其中Ck是常数,1≤i,j≤N,1≤k≤M,解决的问题是求一组解,X1=a1,X2=a2~~~Xn=an,使得所有的约束条件都得到满足。

      差分约束系统的每一个约束条件Xi-Xj≤Ck可以变形为最短路中的三角不等式,Xi≤Xj+Ck,因此,可以把每一个变量Xi看作有向图中的一个结点i,对于每一个约束条件Xi-Xj≤Ck,为从结点j想结点i连一条长度为Ck的有向边

    二、例题

      UVA1723 Intervals

      此题是差分约束的裸题,但我之前也不知道差分约束系统是什么。

      定义:差分约束系统是一种特殊的N元一次不等式,包含N个变量及M个约束条件,每个约束条件由两个变量作差构成,形如Xi-Xj>=Ck,Ck是常数,1<=i,j<=N,1<=k<=M。如

      X1-X0>=2
      X2-X1>=3
      X3-X2>=2
      X3-X0>=6

      通过消元可得到X3-X0的最小值为7。假若我们从0->1建一条权值为2的边,从1->2建一条权值为3的边,以此类推。从0到3跑一遍SPFA求最长路,发现也为7。

      这不是个巧合,因为求最长路时,SPFA的松弛操作后,dis[v]>=dis[u]+g[i].w,与不等式Xi>=Xj+Ck性质一样,所以SPFA可以求出最小值,又因为不等式同大取大,所以要求出最长路。所以SPFA死了吗?

      最后,一定要注意输出,每个答案空一行,最后一个答案最后要输出换行。

      本题就是从0-50000选出最少的数,使每个区间至少有c个数被选,这是求最小值,所以我们待会要将所有的不等式转换成">="的形式 我们用数组d[i]表示0-i之间至少要选多少个数,所以对于每一个约束条件,可以看成d[b_i]-d[a_i-1]>=c_id[bi]d[ai1]>=ci

      d[bi]>=ci+d[ai1]

      因此我们在a[i-1]和b[i]之间建一条长度为c[i]的边

      上述建边过程只是题干中给出的条件,我们还要找出题目中的隐藏条件(隐藏条件视题目而为,有则加)。

      因为d[i]表示的是0-i之间要选的最小个数,所以d[i]一定要大于d[i-1];又因为每个数只能选一次,所以d[i]-d[i-1]要么为0,要么为1;

      由上述两个条件,我们对应出两个不等式d_i-d_{i-1}>=0didi1>=0d_{i-1}-d_{i}>=-1di1di>=1

      所以我们在i-1和i之间连长度为0的有向边,在i和i-1之间连长度为-1的有向边

      因为i的范围是0-50000所以i-1会变成负数,我们都知道,若数组的下标为负,程序会运行错误,所以我们不妨给所有的变量加1,这样就不会出现负下标了,所以上述加边过程可简述为

        1---->在a[i]和b[i]+1之间连一条长度为c[i]的有向边
    
        2---->在i和i+1之间连一条长度为0的有向边
    
        3---->在i+1和i之间连一条长度为-1的有向边

      接着,设d[0]为1,以0为起点求单元最长路

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    int t,n,head[500005],tot,ma,dis[500005];
    bool vis[500005];
    struct node{
        int u,v,next,w;
    }g[5000005];
    inline int rea(){//快读
        int s=0,g=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-')
                g=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            s=(s<<3)+(s<<1)+(ch^48);
            ch=getchar();
        }
        return s*g;
    }
    inline void add(int u,int v,int w){//建图
        g[++tot].u=u;
        g[tot].v=v;
        g[tot].w=w;
        g[tot].next=head[u];
        head[u]=tot;
    }
    inline void spfa(){//SPFA求最长路,即可得出最小值
        queue<int>q;
        q.push(0);
        vis[0]=true;
        dis[0]=0;
        while(!q.empty()){
            int u=q.front();
            q.pop();
            vis[u]=false;
            for(int i=head[u];i;i=g[i].next){
                int v=g[i].v;
                if(dis[v]<dis[u]+g[i].w){
                    dis[v]=dis[u]+g[i].w;
                    if(!vis[v]){
                        vis[v]=true;
                        q.push(v);
                    }
                }
            }
        }
    }
    int main(){
        t=rea();
        while(t--){
            memset(head,0,sizeof(head));
            memset(dis,-0x3f,sizeof(dis));
            tot=0;
            n=rea();
            for(int i=1;i<=n;i=-~i){
                int a=rea(),b=rea(),c=rea();
                add(a,b+1,c);//因为前缀和中,Sb-S(a-1)>=c,然而a-1要大于等于0,a有可能是0,所以我们加一
                ma=max(ma,b+1);
            }
            for(int i=1;i<=ma;i=-~i){
                add(i-1,i,0);
                add(i,i-1,-1);//Si-S(i-1)>=0,S(i-1)-Si>=-1
            }
            spfa();
            printf("%d
    ",dis[ma]);
            if(t)
                printf("
    ");//注意输出!!!
        }
        return 0;
    }
     

    三、相关文章链接

      https://blog.csdn.net/dragon60066/article/details/80245797

  • 相关阅读:
    some Rails leanrning:Rails Ajax,Validates,Cycle
    快速排序与挑白菜
    九月开学
    搜索引擎代码资源
    start RubyOnRails
    垂直搜索引擎之我见
    demo下载:ASP.NET GDI+生成动态的Gif动画
    NHibernate和ADO.Net的性能对比相差有多大?
    手工调试自定义控件各主要方法执行顺序(分运行时和设计时)
    ASP.Net中的缓存方案(不仅仅是Cache和Session)(我在CSDN上和别人的争论)
  • 原文地址:https://www.cnblogs.com/SeanOcean/p/11246604.html
Copyright © 2020-2023  润新知