差分约束系统详解

本文详细介绍了差分约束系统,包括其原理和如何转化为图论最短路问题。针对求最小解和最大解的情况,分别阐述了建图策略,并通过实例分析了如何应用这些方法解决具体的POJ和ZOJ题目。

差分约束问题

差分约束系统

给出一些形如x-y<=b不等式的约束,问是否有满足问题的解,或者求最小,最大解。
这个问题的神奇之处是可以转化为图论的最短路问题。

原理

对于图论的最短路径,有:对于d(v) <= d(u) + w(u, v) ,而差分约束系统的解法利用到了单源最短路径问题中的三角形不等式。
移项得:d(v) – d(u) <= w(u, v),是不是和上面的x-y<=b的一样?
是的,这就是转化为最短路径算法的原理。

建图

1.求最小解

有一个序列,题目用n个整数组合 [ai,bi,ci]来描述它,[ai,bi,ci]表示在该序列中处于[ai,bi]这个区间的整数至少有ci个。如果存在这样的序列,请求出满足题目要求的最短的序列长度是多少。

我们可以设s[i]为从[1,i]闭区间内的整数个数。那么显然对于组合[a,b,c] 我们有 s[b] – s[a-1] >= c (a-1是因为我们需要算上a)还有:0<= s[b]-s[b-1] <=1

变形得
S[b]-S[b-1] >=0
S[b-1]-S[b] >=-1

接着,对于求最小值的来说,我们用最长路来求解。为什么是最长路?一开始把距离d设为无穷小,之后最长路的更新公式为:if(d[v] < d[u]+w(u,v)) d[v]=d[u]+w(u,v); 可以看到d[v]不断的增大,即当满足条件的时候为最小的解。

所以我们在建图的时候,需要满足最长路的三角不等式: d(v) >= d(u) + w(u, v) =>d(v) – d(u) >=w(u,v) 的形式=>从u到v连接一条权重为w(u,v)的边。找出不等关系解出即可。

2.求最大解

一些母牛按升序排成一条直线。有两种要求,A和B距离(A<B)不得超过X,还有一种是C和D(C>D)距离不得少于Y,问可能的最大距离。如果没有输出-1,如果可以随便排输出-2,否则输出最大的距离。

题目按升序排,所以有:
s[i] – s[i-1] >=0
由题目的约束条件得到:
B – A <= X
D – C >= Y
因为题目求的是最大距离,故用最短路来求解
最短路三角不等式为 d(v) <= d(u) + w(u,v) => d(v) – d(u) <= w(u,v) =>从u到v连接一条权重为w(u,v)的边
所以我们都转化为小于等于的符号,所以有:
s[i-1] – s[i] <=0
B – A <= X
C – D <= -Y
建完图后SPFA即可。(有负环说明无解输出-1 , 1与n不连通说明可以随意摆放,没有约束嘛。输出-2,否则输出dis [n])

题目

poj3159:求最大解

这个没什么好说的,唯一想吐槽的是队列SPFA要T而栈式SPFA可以过,还有就是poj scanf比读入优化快。

zoj1770:求最小解(SPFA判环)

同上,判负环条件:一个点入队超过n次。

poj2983:判断是否可行(图不一定连通)

加入源点s向每一个点连接权值为0的边,之后判负环。

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int Maxn=1e3+50,Maxm=5e5+50;

int n,m,dis[Maxn],last[Maxn],to[Maxm],val[Maxm],before[Maxm],in[Maxn],vis[Maxn],ecnt=1;
queue<int>q;
char ch[20];

inline void add(int x,int y,int c)
{
    before[++ecnt]=last[x];
    last[x]=ecnt;
    to[ecnt]=y;
    val[ecnt]=c; 
}

int a,b,x;
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        ecnt=1;
        memset(in,0,sizeof(in));
        memset(last,0,sizeof(last));
        memset(dis,0x3f,sizeof(dis));
        int bz=1;
        for(int i=1;i<=m;i++)
        {
            scanf("%s",ch+1);
            if(ch[1]=='P')
            {
                scanf("%d%d%d",&a,&b,&x);
                add(b,a,x);add(a,b,-x);
                if(a==b&&x!=0)bz=0;
            }
            else
            {
                scanf("%d%d",&a,&b);
                add(a,b,-1);
                if(a==b)bz=0;
            }
        }
        for(int i=1;i<=n;i++)add(n+1,i,0);
        dis[n+1]=0;q.push(n+1),vis[n+1]=1;
        while(!q.empty())
        {
            int u=q.front();q.pop();vis[u]=0;
            for(int e=last[u];e&&bz;e=before[e])
            {
                if(dis[to[e]]>dis[u]+val[e])
                {
                    dis[to[e]]=dis[u]+val[e];
                    if(!vis[to[e]])
                    {
                        in[to[e]]++;
                        if(in[to[e]]>n)bz=0;
                        dis[to[e]]=dis[u]+val[e];
                        q.push(to[e]);
                    }
                }
            }
        }
        if(bz)puts("Reliable");
        else puts("Unreliable");
    }
} 
poj1275:二分判断解性

主要是将总人数a二分并添加条件:sum≥a.

poj1364: 判断合法性

两个坑点:1.将大于小于转化为大等于小等于。2.没有s[i]-s[i-1]≥0这个条件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值