zoukankan      html  css  js  c++  java
  • 图论:最短路-差分约束

    如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统

    举一个例子,给定n个变量和m个不等式,每个不等式的形式为x[i]-x[j]<=a[k](0<=i,j<n,0<=k<m,a[k]已知)求x[i]-x[j]的最大值

    为了解决这一类问题,需要先将所有的约束条件统一一下格式:

    如果需要求的是两个变量差的最大值,那么需要将所有不等式转变成"<=",建图后求最短路

    对于每个不等式x[i]-x[j]<=a[k],对结点j和i建立一条j->i的有向边,边权为a[k],求x[n-1]-x[0]的最大值就是求0到n-1的最短路

    如果需要求的是两个变量差的最小值,那么需要将所有不等式转化成">=",建图后求最长路

    下面一BZOJ2330为例,这是一个可以转化为差分约束问题的求最小值的一个例子

    const int maxn=100005;
    int n,k,cnt;
    long long ans;
    int q[maxn],head[maxn],d[maxn],cir[maxn];
    bool inq[maxn];
    struct data{int to,next,v;}e[4*maxn];
    void insert(int u,int v,int w)
    {e[++cnt].to=v;e[cnt].next=head[u];e[cnt].v=w;head[u]=cnt;}

    n个小朋友,k个限制条件,cnt是邻接表边的数量

    ans是最终的答案,q,head,d是最短路相关的数组(此处是求的最长路),cir是用来统计每个点的入队次数的,用来判环

    bool spfa()
    {
        int h=0,t=1,now;
        q[t]=0,inq[0]=1;cir[0]=1;
        while(h!=t)
        {
            h=h%maxn+1;
            now=q[h];
            for(int i=head[now];i;i=e[i].next)
            {
                if(d[now]+e[i].v>d[e[i].to])
                {
                    d[e[i].to]=d[now]+e[i].v;  //最长路
                    if(++cir[e[i].to]>=n) return 0;
                    if(!inq[e[i].to])
                    {
                        inq[e[i].to]=1;
                        t=t%maxn+1;q[t]=e[i].to;
                    } 
                }
            }
            inq[now]=0;
        }
        return 1;
    }

    这里的spfa是可以判环的,并且求的是最长路,这点需要注意

    然后,针对每一个限制条件:

                case 1:insert(a,b,0);insert(b,a,0);break;//b>=a a>=b -> a=b
                case 2:if(a==b){printf("-1");return 0;}
                    insert(a,b,1);break;//b>=a+1 -> b>a
                case 3:insert(b,a,0);break;//a>=b
                case 4:if(a==b){printf("-1");return 0;}
                    insert(b,a,1);break;//a>=b+1 -> a>b
                case 5:insert(a,b,0);break;

    因为题目要求的是,所有人分得糖果的和的最小值,那么需要假设一个虚拟节点0,然后以0作为源点计算从源点到所有点的最长路

    之后我统计d数组就可以得到0到每一个节点的距离,这个距离值可以转化成差分约束条件:

    求A-B的最小值就是求B到A的最长路,那么本题中,计算的任一点x到0的最长路dx也就是x-0>=dx,那么就有x>=dx,dx就因此成了x的下界

    遇到最值问题的时候,找到合适的源点,分清是求最大值还是最小值,将差分约束条件进行合理地转化是解决问题的关键

    这里给出利用差分约束系统求最大值并结合最“短”路模型时的几种约束条件的变式:

    X[n-1]-X[0]>=T ,可以进行移项转化为: X[0]-X[n-1]<=-T。
    X[n-1]-X[0]<T, 可以转化为X[n-1]-X[0]<=T-1。
    X[n-1]-X[0]=T,可以转化为X[n-1]-X[0]<=T&&X[n-1]-X[0]>=T,再利用第一种方式进行转化即可

    下面给出题目完整的实现:

     1 #include<cstdio> 
     2 const int maxn=100005;
     3 int n,k,cnt;
     4 long long ans;
     5 int q[maxn],head[maxn],d[maxn],cir[maxn];
     6 bool inq[maxn];
     7 struct data{int to,next,v;}e[4*maxn];
     8 void insert(int u,int v,int w)
     9 {e[++cnt].to=v;e[cnt].next=head[u];e[cnt].v=w;head[u]=cnt;}
    10 bool spfa()
    11 {
    12     int h=0,t=1,now;
    13     q[t]=0,inq[0]=1;cir[0]=1;
    14     while(h!=t)
    15     {
    16         h=h%maxn+1;
    17         now=q[h];
    18         for(int i=head[now];i;i=e[i].next)
    19         {
    20             if(d[now]+e[i].v>d[e[i].to])
    21             {
    22                 d[e[i].to]=d[now]+e[i].v;  //最长路
    23                 if(++cir[e[i].to]>=n) return 0;
    24                 if(!inq[e[i].to])
    25                 {
    26                     inq[e[i].to]=1;
    27                     t=t%maxn+1;q[t]=e[i].to;
    28                 } 
    29             }
    30         }
    31         inq[now]=0;
    32     }
    33     return 1;
    34 }
    35 int main()
    36 {
    37     scanf("%d%d",&n,&k);
    38     int x,a,b;
    39     while(k--)
    40     {
    41         scanf("%d%d%d",&x,&a,&b);
    42         switch(x)
    43         {
    44             case 1:insert(a,b,0);insert(b,a,0);break;//b>=a a>=b -> a=b
    45             case 2:if(a==b){printf("-1");return 0;}
    46                 insert(a,b,1);break;//b>=a+1 -> b>a
    47             case 3:insert(b,a,0);break;//a>=b
    48             case 4:if(a==b){printf("-1");return 0;}
    49                 insert(b,a,1);break;//a>=b+1 -> a>b
    50             case 5:insert(a,b,0);break;
    51         }
    52     }
    53     for(int i=n;i>=1;i--) insert(0,i,1);//i>=0+1 -> i>0
    54     if(!spfa()) {printf("-1");return 0;}  //有环
    55     for(int i=1;i<=n;i++) ans+=d[i];  //每一个点与0的距离就是每一个人分到的糖果数 
    56     printf("%lld",ans); 
    57     return 0;
    58 }
  • 相关阅读:
    【作业】5个不合理的Xaml Control
    记录一下 mysql 的查询中like字段的用法
    BaseDao代码,用于连接数据库实行增删改查等操作
    Java的多态
    Java基础一
    数据库的设计
    JavaScript基础
    我注册博客的第一天
    JAVA异常处理流程理解
    javaweb中解决中文乱码问题
  • 原文地址:https://www.cnblogs.com/aininot260/p/9398280.html
Copyright © 2011-2022 走看看