差分约束系统

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

\[x_i-x_j\leq b_k(i,j\in [1,n],k\in (1,m) \]

或者

\[x_i-x_j\geq b_k(i,j\in [1,n],k\in (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_i\leq -1\)
所以共有两种类型的不等式:

\[\left\{\begin{matrix}max(a_{b_i},a_{b_{i-1}})-min(a_{b_i},a_{b_{i-1}})\leq d\\ a_{i-1}-a_i\leq -1\end{matrix}\right.\]

由于求解的是最大值,所以建图通过\(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\n",dis[max(mini,maxi)]);
        }
        else printf("-1\n");
    }
    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\)
同时存在不等式\(0\leq S_i-S_{i-1}\leq 1\),拆分成两个不等式\(S_i\geq S_{i-1}+0\)\(S_{i-1}\geq S_i-1\)
所以共有三种不等式:

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

由于求解的是最小值,所以建图通过\(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\n",dis[maxi]);
    }
    return 0;
}
posted @ 2020-08-23 00:36  fxq1304  阅读(70)  评论(0编辑  收藏  举报