【模板】差分约束

差分约束问题:

给定$n$个变量$\{x_0,x_1,x_2,\ldots,x_{n-1}\}$和m个形如$x_i-x_j\leq a_i(x_i-x_j\geq a_i)$的不等式,求$x_{n-1}-x_0$的最大(小)值。

例如$n=4,m=5$,不等式如图,求$x_3-x_0$的最大值。

考虑初中数学方法,我们可以通过不等式相加得到一些形如$x_3-x_0\leq b_i$的不等式,易得$min\{b_i\}$即为所求最大值。

于是暴力手算得到以下三个不等式:

1.由$(5)+(4)+(1)$得到$x_3-x_0\leq7$。

2.由$(5)+(2)$得到$x_3-x_0\leq9$。

3.由$(3)$得到$x_3-x_0\leq8$。

联立解得$x_3-x_0\leq7$,最大值为$7$。

我不知道是否有人认为手算十分简便,不过我现在写起来感觉十分恶心。

那么如何系统解决这类问题呢?

考虑另一个问题,给定一张有四个点,五条边的有向图,如图所示,

 

(图源网络,侵删)

求第$0$个点到第$3$个点的最短路。

显然答案是$7$,根本不用进行什么演算。

那么这两个问题间有什么关联呢?

考虑我们将第一个问题中一些不等式的和转化为$x_{n-1}-x_0\leq b_i$的过程,

若只转换成一个不等式,即由$x_{n-1}-x_i\leq a_i,x_i-x_j\leq a_j,\ldots,x_k-x_0\leq a_0$求和可得$x_{n-1}-x_0\leq \sum a_i$。

那么这样的一个过程是否等价于,

在图上沿$x_0\rightarrow x_k,\ldots,x_j\rightarrow x_i,x_i\rightarrow x_{n-1}$这些边走出的一条$x_0$到$x_{n-1}$,花费$\sum a_i$的路径?

显然的,转换不等式的过程中等式右边的值每次增加$a_i$,最终为$x_{n-1}-x_0\leq \sum a_i$,所求的是最大值所以等号取等,两者便等价了。

那么我们推广成$K$个不等式的情况,即由$x_{n-1}-x_0\leq b_0,x_{n-1}-x_0\leq b_1,\ldots,x_{n-1}-x_0\leq b_K$这些不等式

转化成图上$x_0\rightarrow x_{n-1}$长度为$b_0,b_1,\ldots,b_K$的路径。

此时可以发现$max\{x_{n-1}-x_0\}=min\{b_i\}$,即图上从$x_0$到$x_{n-1}$的最短路径的长度。

问题转化成功。现在我们只需要打一个最短路板子就可以解决这个手算量$MAX$的差分约束问题了。

 


 

一般地,差分约束问题总可以转化为最短(长)路问题求解,

将每个形如$x_i-x_j\leq a_i(x_i-x_j\geq a_i)$的不等式转化成图上$j\rightarrow i$,权值为$a_i$的边,

以$0$为起点进行单原最短(长)路算法求解即可,答案为$dis_{n-1}$。

(以最短路为例)最终图中必定满足对于任意$i,j$,如果有$j\rightarrow i$,权值$a_i$的边存在,则$dis_i\leq dis_j+a_i$,严格满足差分约束。

反之,如果$0\rightarrow n-1$的路径中存在负环(存在形如$x_i-x_j\geq a$与$x_i-x_j<a$的一组等式)

或者$0$和$n-1$不连通($x_{n-1}$不受$x_0$的约束条件影响),则原问题无解。

在算法竞赛中,差分约束系统的不等式约束关系一般是不明显的,需要我们自己抽象出模型进行解答。

 


 

模板题目:poj1201

题意:从 $0\sim 5\times 10^4$ 中选出尽量少的整数,使每个区间 $[a_i,b_i]$ 内都有至少 $c_i$ 个数被选出。

题解:设 $s_i$ 表示 $0\sim i$ 中选了多少整数,然后转化成三角形不等式即可。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>

using namespace std;
#define MAXN 1000005
#define MAXM 50000
#define INF 0x7fffffff
#define ll long long

int hd[MAXN],to[MAXN<<1],cnt;
int cst[MAXN<<1],nxt[MAXN<<1];
int dis[MAXN];bool inq[MAXN];

inline int read(){
    int x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x*f;
}

inline void addedge(int u,int v,int w){
    to[++cnt]=v,cst[cnt]=w;
    nxt[cnt]=hd[u],hd[u]=cnt;
    return;    
}

inline void SPFA(int s){
    memset(dis,-63,sizeof(dis));
    queue<int> q;q.push(s);
    dis[s]=0,inq[s]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop();inq[u]=0;
        for(int i=hd[u];i;i=nxt[i])
            if(dis[to[i]]<dis[u]+cst[i]){
                dis[to[i]]=dis[u]+cst[i];
                if(!inq[to[i]])
                    inq[to[i]]=1,q.push(to[i]);
            }
    }
    return;    
}

int main(){
    int N=read();
    for(int i=1;i<=N;i++){
        int u=read(),v=read(),w=read();
        addedge(u-1,v,w);
    }
    for(int u=0;u<=MAXM;u++)
        addedge(u,u+1,0),addedge(u+1,u,-1);
    SPFA(0);
    printf("%d\n",dis[MAXM]);
    return 0;
}

 

转载于:https://www.cnblogs.com/YSFAC/p/9665665.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值