差分约束系统

差分约束系统
差分约束系统是求解关于一组变量的特殊不等式组的方法,系统由(n)个变量和(m)个不等式组成,其中每个不等式的形式为

[x_i-x_jleq b_k(i,jin [1,n],kin (1,m) ]

或者

[x_i-x_jgeq b_k(i,jin [1,n],kin (1,m) ]

分别对应两数差的最大值(最短路)和最小值(最长路)
差分约束系统的求解方法
差分约束系统可以转化为图论的最短路或者最长路解决
由于在最短路算法的松弛过程中,(dis[v]leq dis[u]+w(u,v)),也就是(dis[v]-dis[u]leq w(u,v)),与差分约束系统中不等式的形式相似,所以对于每个不等式,从(x_j)(x_i)连接一条边。计算(x_n-x_1)的最大值,等价于计算从(x_1)(x_n)的最短路
类似地,计算(x_n-x_1)的最小值,等价于计算从(x_1)(x_n)的最长路
所以首先可以根据求解的是最大值还是最小值,改变不等号的方向,使得不等式组中所有不等号的方向一致。最小值对应的不等号为(leq),最大值对应的不等号为(geq)
建图之后不一定存在最短路或者最长路,也就是差分约束系统不一定有解,判断是否有解等价于判断图中是否存在负环((Bellman-Ford)或者(SPFA)
相关题目
hdu3440 House Man 求解最大值
给出一列数作为房屋高度,其中所有的高度值都不相同,按照高度从低到高的顺序逐个在房顶上跳,每个房屋被跳一次,每次跳的两个房屋之间的距离不超过(d)。同一个位置最多只有一个房屋,在不改变原来房屋排列顺序的情况下安排所有房屋之间的距离,计算最低的房屋和最高的房屋之间距离的最大值。

设高度为(h)的房屋在给定的排列中的顺序为(a_h),按照高度排序之后第(i)个房屋的高度为(b_i)。由于高度相邻的两个房屋之间的距离不超过(d),所以有(max(a_{b_i},a_{b_{i-1}})-min(a_{b_i},a_{b_{i-1}})leq d)
由于同一个位置最多只有一个房屋,所以有(a_i-a_{i-1}geq 1),也就是(a_{i-1}-a_ileq -1)
所以共有两种类型的不等式:

[left{egin{matrix}max(a_{b_i},a_{b_{i-1}})-min(a_{b_i},a_{b_{i-1}})leq d\ a_{i-1}-a_ileq -1end{matrix} ight.]

由于求解的是最大值,所以建图通过(spfa)算法计算从(min(a_{b_1},a_{b_n}))(max(a_{b_1},a_{b_n}))的最短路

#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<string>
#include<sstream>
#include<cmath>
#include<ctime>
#include<algorithm>
#define LL long long
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define pi acos(-1.0)
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;

const int maxn=1010;
int T,n,d,head[maxn],to[4*maxn],nxt[4*maxn],weight[4*maxn],cnt;
int inq[maxn],k[maxn];
LL a[maxn],b[maxn],dis[maxn];
map<LL,int> mp;

void add(int u,int v,int w){
    to[cnt]=v;
    weight[cnt]=w;
    nxt[cnt]=head[u];
    head[u]=cnt++;
}

bool spfa(int s){
    for(int i=1;i<=n;i++){
        dis[i]=(i==s)?0:1e18;
        inq[i]=(i==s)?1:0;
        k[i]=(i==s)?1:0;
    }
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int p=q.front();
        q.pop();
        inq[p]=0;
        for(int i=head[p];~i;i=nxt[i]){
            int v=to[i],w=weight[i];
            if(dis[v]>dis[p]+w){
                dis[v]=dis[p]+w;
                k[v]++;
                if(k[v]>=n) return true;
                if(!inq[v]){
                    inq[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return false;
}

int main(){
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
        scanf("%d %d",&n,&d);
        mp.clear();
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            mp[a[i]]=i;
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i=2;i<=n;i++){
            int u=mp[b[i-1]];
            int v=mp[b[i]];
            add(min(u,v),max(u,v),d);
        }
        for(int i=2;i<=n;i++){
            add(i,i-1,-1);
        }
        printf("Case %d: ",cas);
        int mini=mp[b[1]];
        int maxi=mp[b[n]];
        if(!spfa(min(mini,maxi))){
            printf("%lld
",dis[max(mini,maxi)]);
        }
        else printf("-1
");
    }
    return 0;
}

hdu1384 Intervals 求解最小值
给出(n)个区间,每个区间为([a_i,b_i]),选取一些数,使得区间(i)中的数的个数不少于(c_i),计算选取的数的个数的最小值

(S_i)表示([1,i])中选取的数的个数的最小值,则对于每一个区间([a_i,b_i]),有不等式(S_{b_i}-S_{a_i-1}geq c_i)
同时存在不等式(0leq S_i-S_{i-1}leq 1),拆分成两个不等式(S_igeq S_{i-1}+0)(S_{i-1}geq S_i-1)
所以共有三种不等式:

[left{egin{matrix}S_{b_i}-S_{a_i-1}geq c_i\ S_igeq S_{i-1}+0\ S_{i-1}geq S_i-1end{matrix} ight.]

由于求解的是最小值,所以建图通过(spfa)算法计算从(S_{mini})(S_{maxi})的最长路

#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<string>
#include<sstream>
#include<cmath>
#include<ctime>
#include<algorithm>
#define LL long long
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define pi acos(-1.0)
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;

const int maxn=50010,inf=0x3f3f3f3f;
int n,head[maxn],nxt[4*maxn],to[4*maxn],weight[4*maxn],cnt,inq[maxn];
LL dis[maxn];
queue<int> q;

void add(int u,int v,int w){
    to[cnt]=v;
    weight[cnt]=w;
    nxt[cnt]=head[u];
    head[u]=cnt++;
}

void spfa(int mini,int maxi){
    for(int i=mini+1;i<=maxi;i++){
        dis[i]=-1e18;
        inq[i]=0;
    }
    dis[mini]=0;
    inq[mini]=1;
    q.push(mini);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        inq[u]=0;
        for(int i=head[u];~i;i=nxt[i]){
            int v=to[i],w=weight[i];
            if(dis[v]<dis[u]+w){
                dis[v]=dis[u]+w;
                if(!inq[v]){
                    q.push(v);
                    inq[v]=1;
                }
            }
        }
    }
}

int main(){
    while(~scanf("%d",&n)){
        memset(head,-1,sizeof(head));
        cnt=0;
        int mini=inf,maxi=-1;
        for(int i=1;i<=n;i++){
            int u,v,w;
            scanf("%d %d %d",&u,&v,&w);
            add(u-1,v,w);
            mini=min(mini,u-1);
            maxi=max(maxi,v);
        }
        for(int i=mini+1;i<=maxi;i++){
            add(i-1,i,0);
            add(i,i-1,-1);
        }
        spfa(mini,maxi);
        printf("%lld
",dis[maxi]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/fxq1304/p/13547901.html