差分约束系统

差分约束系统

简单来说,差分约束是用图论中的最短路解决一些不等式(组)。

例如:Xi表示序列第i个数,请求出一组满足:X1-X5 <= 1, X1 - X3 <= 3, X2 - X4 <= -2的长度为5的序列

怎么建图?举个栗子,X1-X5 <= 1, 就从结点5向结点1连一条权为1的边。

X1-X5 <= 1  ->  X1 <= X5 + 1

联想到最短路的性质:Dis(i) <= Dis(j) + edge(j, i).(dis_i是源点到i距离)

两个一一对应一下,把X1看成Dis(1),X5看成Dis(5),1就是这两个结点连出的边权,因此对于每一对Xi - Xj <= Wk, 就从j到i连一条权是Wk的边即可。

为了图的连通性(为了有个源点)最后再新建一个结点0,连到每个结点,长度是0(即Xi<=X0+0,因为X0不是题目中的结点,所以随意约定它的值不会影响答案)

从结点0跑单源最短路,满足这一系列不等式的的序列就是最后的Dis(1) 到 Dis(n)。

图中有负环时无解

刚刚讲的是Xi - Xj <= Wk的形式,直接j到i连一条权为Wk,如果是 Xi - Xj >= Wk 的话,两边乘以-1,转换成Xj - Xi <= -Wk,所以最后的方法是:i到j连一条权为-Wk的边。

如果是Xi - Xj = Wk 呢?我们就建两条边,分别是Xi - Xj ≥ Wk ,Xi - Xj ≤ Wk。


练习

例1∶Luogu P3275 糖果

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 if(x == 5) add(a,b,0);
if(x % 2 && a == b){
    printf("-1"); //同一人,但糖数不等,不符合逻辑
    return 0;
}
bool SPFA(int x){
    memset(dis,0x3f,sizeof(dis));
    int k;
    s.push(x);
    visit[x] = 1;
    dis[x] = 0;
    while(!s.empty()){
        k = s.front();
        visit[k] = 0;
        s.pop();
        for(int i=head[k];i!=-1;i=edge[i].next)
            if(dis[edge[i].to] > dis[k] + edge[i].w){
                dis[edge[i].to] = dis[k] + edge[i].w;
                if(!visit[edge[i].to])s.push(edge[i].to),visit[edge[i].to] = 1,use[edge[i].to]++;
                if(use[edge[i].to]>n-1)return false;
            }    
    }
    return true;
}

例2∶Luogu P1250 种树

这类题又有些微的不同,Sum(n)是前n个单位种的树的棵树,好比前缀和,所以Sum(k+1)一定大于等于Sum(k)

1.Sum(k+1) ≤ Sum(k)

2.Sum(k+1) - Sum(k) ≤ 1

于是我们这样建边:

for(int i=1;i<=n;i++) add(i-1,i,1),add(i,i-1,0);

此时还能为了图的连通将0与每个点相连吗?

若建边用add(0,i,0),则按照题意就是 Sum(i)  -Sum(0) ≤ 0,这种建边方式不适用于这种类似前缀和的题。

于是我们只好选择点 L+1以保持图的连通。

for(int i=1;i<=n;i++) add(n+1,i,0);

最后,我们要求的是种最少的棵数,然而我们求出的关系是相对的。

举个栗子,3,4,5,9,2是一种方案,那么1,2,3,7,0是同样成立的。

于是我们最后要求的最少个数需要减去最小值

for(int i=0; i<=n; i++) minn = min(minn, dis[i]);
cout << dis[n] - minn << endl;

 例3:排队

某些粉丝之间互相喜欢,他们希望互相之间的距离至多为一个定值。但某些粉丝乊间互相厌恶,他们希望互相之间的距离至少为一个定值。现在给定K个互相喜爱的粉丝对以及他们乊间距离的最大值,L个互相厌恶的粉丝对以及他们之间距离的最小值。
你的任务是计算在满足以上条件的前提下,帮劣YSY计算出编号为1和编号为N的粉丝乊间距离的最大可能值。

【输入】
输入文件为 layout.in。
第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 N,K和L ;
此后K行,每行包含三个用空格分开的整数A, B和D,其中A,B满足1<=A<=B<=N。表示编号为A和B的粉丝之间的距离至多为D。
此后L行,每行包含三个用空格分开的整数A,B和D,其中A,B满足1<=A<=B<=N。表示编号为A和B的粉丝之间的距离至少为D。
【输出】
输出文件名为 layout.out。
输出文件仅包含一个整数。如果丌存在任何吅法的排队方式,就输出-1。如果编号1和编号N的粉丝间距离可以任意,就输出-2 。否则输出他们之间的最大可能距离。

【输入输出样例】
layout.in

4 2 1
1 3 10
2 4 20
2 3 3

layout.out

27

【数据范围】

对于40%的数据,N<=100;
对于100%的数据,N<=1000;K, L<=10000;D<=1000000。

此题和上两题又不一样了,前两题不需要连通,那么我们新加一个点,连接每一个点以保证连通。

题意中“如果编号1和编号N的粉丝间距离可以任意,就输出-2”,说明图必须连通,所以我们只能选择现有的点。

for(int i=1;i<=n;i++) add(i,i-1,0);//Sum(i)>=Sum(i-1) 
if(!SPFA(1)) printf("-1");
else if(vis[n] == 0)printf("-2");
else printf("%lld",dis[n]);

 

posted @ 2019-07-28 19:35  Cindy_Chan  阅读(582)  评论(0编辑  收藏  举报