差分约束算法

概括

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

求不等式的可行解

源点需要满足的条件: 从源点出发,一定可以走到所有的边
否则 用单源最短路做的话 有一条边走不到 则该边对应的不等式就无法满足

  1. 把每个x[i] ≤ x[j] + C[k]不等式转化为一条从 x[j] 走到 x[i] 长度为 C[k] 的边

  2. 然后在这个图上找一个超级源点,使得该源点一定可以遍历到所有边

  3. 从源点求一遍单源最短路
    3.1假如存在负环

    x[2] ≤ x[1] + c[1]

    x[k] ≤ x[k-1] + c[k-1]
    x[1] ≤ x[k] + c[k]
    对第一个不等式用后面的不等式一直做松弛
    x[2] ≤ x[1]+c[1]
        ≤ x[k] + c[k] + c[1]
        ≤ x[k-1] + c[k-1] + c[k] + c[1]
       …
        ≤ x[2] + c[2] +…+ c[k-1] + c[k] + c[1]
        ≤ x[2] + (小于零的Σc[i])
    x[2] < x[2]

    矛盾

    得出结论:不等式无解 即存在负环

4.求完单源最短路之后
 存在负环 => 不等式无解
 没有负环 => 求完之后一定是满足这个不等式的 <=> 即一个可行解

求最大值或最小值

结论 : 如果求的是最小值,则应该求最长路,如果求的是最大值,则应该求最短路

问题 : 如何转化 x[i] ≤ c (其中 c 是一个常数) 这类的不等式
方法 : 建立一个超级源点, 0 号点 x[0] ,然后建立 0→i 长度是 c 的边即可

x[i] ≤ c <=> x[i] ≤ x[0] + c = 0 + c

以求 x[i] 的最大值为例:所有从 x[i] 出发,构成的不等式链

x[i] ≤ x[j] + c[1] ≤ x[k] + c[2] + c[1] ≤⋅⋅⋅≤ x[0] + c[1] + c[2] +⋅⋅⋅+ c[m] (x0=0)

所计算出的上界,最终 x[i] 的最大值 = 所有上界的最小值

那么求x[i]最大值

求所有上界的最小值

求所有从 0→i 的路径和的最小值

最短路求 dist[i]

例题

题目概括

幼儿园里有 N 个小朋友,老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。
但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候, 老师需要满足小朋友们的 K 个要求。
幼儿园的糖果总是有限的,老师想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。

输入

输入的第一行是两个整数 N,K。
接下来 K 行,表示分配糖果时需要满足的关系,每行 3 个数字 X,A,B。
如果 X=1.表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多。
如果 X=2,表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果。
如果 X=3,表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果。
如果 X=4,表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果。
如果 X=5,表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果。
小朋友编号从 1 到 N。

输出

输出一行,表示老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 −1。

由题我们可以转换成不等式

  1. A = B <=> A≥B B≥A
  2. A < B <=> B≥A+1
  3. A≥B <=> A≥B
  4. A > B <=> A≥B+1
  5. B≥A <=> B≥A

每个人都能够分到糖果所以可以得到 x ≥ 1

x ≥ x0 + 1 可以设源点 x0 = 0

代码

#include 
#include 
#include 

using namespace std;

typedef long long LL;

const int N=1e5+10,M=N*3;

int n,k;
int e[M],h[N],ne[M],w[M],idx;
int q[N],dist[N],cnt[N];
bool st[N];

void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

bool spfa()
{
    int hh=0,tt=1;
    memset(dist,-0x3f,sizeof dist);
    q[0]=0;
    dist[0]=0;
    st[0]=true;
    while(hh!=tt)
    {
        int t=q[--tt];
        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]<dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n+1) return false;
                if(!st[j]) 
                {
                    q[tt++]=j;
                    st[j]=true;
                }
            }
        }
    }
    return true;
}

int main()
{
    cin>>n>>k;
    memset(h,-1,sizeof h);
    while(k--)
    {
        int a,b,x;
        cin>>x>>a>>b;
        if(x==1) add(a,b,0),add(b,a,0);
        else if(x==2) add(a,b,1);
        else if(x==3) add(b,a,0);
        else if(x==4) add(b,a,1);
        else add(a,b,0);
    }
    
    for(int i=1;i<=n;i++) add(0,i,1);
    
    if(!spfa()) puts("-1");
    else 
    {
        LL res=0;
        for(int i=1;i<=n;i++) res+=dist[i];
        cout<<res<<endl;
    }
    
    return 0;
}

你可能感兴趣的:(ACM每周知识点,算法竞赛相关,算法竞赛——算法提高课,算法,c++,图论)