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

    差分约束系统解决的是一个N元1次不等式求解的问题,其中每一个不等式都形如Xi-Xj<=Ck,其中Ck为常数。差分约束系统与最短路关系甚密,可以用最短路算法求解,即先对不等式进行移项操作,将不等式转化为Xi<=Xj+Ck,这正是求完单源最短路后应该满足的条件(在没有负环的情况下)。其中每一个变量可以看作距源点的距离,常量就可以看作边权,每一个不等式就可以建一条边。

    差分约束系统求解的技巧和注意事项:

    1)考虑以下不等式组:a>b,b>c,c>a。很明显,这个不等式组无解。那么如何判断一个差分约束系统无解呢?联系上文的差分约束系统建图规则,当我们发现了一个负环的时候,差分约束系统就无解了。

    2)在有不等号方向不同的不等式时,要先通过移项将所有不等式转化为方向相同的不等式。

    3)在全都是>=的不等式组时,如Xi>=Xj+Ck,建边时需要建一条W(j,i)=Ck的边,再通过单源最长路进行求解。

    4)在全都是<=的不等式组时,如Xi<=Xj+Ck,建边时需要建一条W(j,i)=Ck的边,再通过单源最短路进行求解。

    5)在没有负环的情况下,每个Xi=dist[i]+k(k>=0)就是差分约束系统的一组解。

    6)可能出现负边权,需要使用SPFA算法

    下面来一道例题

    这道题目就是一道经典的差分约束系统题目,题目中给定了三种差分约束的条件:

    • 农场a比农场b至少多种植了c个单位的作物,
    • 农场a比农场b至多多种植了c个单位的作物,
    • 农场a与农场b种植的作物数一样多。

    对于这三个约束条件,我们可以列出如下不等式:

    1、对于条件1,易得Xa-Xb>=c,进而得到Xa>=Xb+c;建一条W(b,a)=c的边

    2、对于条件2,易得Xa-Xb<=c,进而得到-Xb<=c-Xa,不等式两边同乘-1得到Xb>=Xa+(-c);建一条W(a,b)=-c的边;

    3、对于条件3,建两条W(a,b)=W(b,a)=0的边。

    建好边后判断有无负环即可,下面给出参考代码(此题基本相当于差分约束系统模板题):

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 int n,m,dist[50005],v[50005],w[50005],nxt[50005],head[50005],cnt,c,x,y,z;
     5 bool instack[50005];
     6 void add(int a,int b,int c)
     7 {
     8     v[++cnt]=b;
     9     w[cnt]=c;
    10     nxt[cnt]=head[a];
    11     head[a]=cnt;
    12 }
    13 bool spfa(int node)
    14 {
    15     instack[node]=1;
    16     for(int i=head[node];i;i=nxt[i])
    17     {
    18         int y=v[i];
    19         if(dist[y]<dist[node]+w[i])
    20         {
    21             dist[y]=dist[node]+w[i];
    22             if(instack[y])return 0;
    23             if(!spfa(y))return 0;
    24         }
    25     }
    26     instack[node]=0;
    27     return 1;
    28 }
    29 int main()
    30 {
    31     cin>>n>>m;
    32     for(int i=1;i<=m;i++)
    33     {
    34         cin>>c;
    35         if(c==1)
    36         {
    37             cin>>x>>y>>z;
    38             //dist[x]>=z+dist[y]
    39             add(y,x,z);
    40         }
    41         if(c==2)
    42         {
    43             cin>>x>>y>>z;
    44             //dist[y]>=-z+dist[x]
    45             add(x,y,-z);
    46         }    
    47         if(c==3){
    48             cin>>x>>y;
    49             add(x,y,0);
    50             add(y,x,0);
    51         }
    52     }
    53     for(int i=1;i<=n;i++)add(0,i,0);
    54     for(int i=1;i<=n;i++)dist[i]=-21370444;
    55     if(spfa(0))cout<<"Yes";
    56     else cout<<"No";
    57     return 0;
    58 }
    View Code
  • 相关阅读:
    关于.NET Reflector
    Windows Debugging之九
    在IA32如何将程序计数器的值放入到整数寄存器中?
    [陆续添加]计算机网络最最基础的基本概念
    ASCII表
    [翻译文章]我们是如何做到的: 提高SharePoint.Microsoft.com站点的性能
    Windows API是什么?
    寄存器使用惯例
    阅读笔记 了解ASP.NET底层架构 之一
    汇编程序中的返回值
  • 原文地址:https://www.cnblogs.com/szmssf/p/11079511.html
Copyright © 2011-2022 走看看