首页 > 其他 > 详细

差分约束系统 初见

时间:2014-11-14 22:41:18      阅读:345      评论:0      收藏:0      [点我收藏+]

今天初次学习差分约束系统,很神奇的东西

定义:

如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。
 
其实在一些问题中需求最小值或最大值,同时需要满足一系列的不等式关系,这样就可以用到差分约束系统了,对于求最小值的问题,可以将问题转化为最长路问题;求最大值可以转化为最短路问题。唯一需要注意的就是要找全题中的不等关系,建立正确的边的关系,通常情况下一遍spfa就可以漂亮的解决较为困难的题目。对于大数据也可以很快的出解。
几个小练习:
CODEVS1653 种树2
题目描述 Description

一条街的一边有几座房子。因为环保原因居民想要在路边种些树。路边的地区被分割成块,并被编号为1…n。每个块的大小为一个单位尺寸并最多可种一裸树。每个居民想在门前种些树并指定了三个号码b,e,t。这三个数表示该居民想在b和e之间最少种t棵树。当然,b≤e,居民必须保证在指定地区不能种多于地区被分割成块数的树,即要求T≤ e-b+1。允许居民想种树的各自区域可以交叉。出于资金短缺的原因,环保部门请你求出能够满足所有居民的要求,需要种树的最少数量。

思路:可以将这个题目借助前缀和数组转化到一系列的不等式关系。若用f[i]表示到i所用的树木数,f[e]-f[b]>=t(建一条从b到e的长度为t的边权),在题目中还有一个隐含条件,就是0<=f[i]-f[i-1]<=1,这个隐含关系中可以将f[i]与f[i-1]与0、1建立关系,又多了很多条边。对于<=的情况可以同时乘-1,就转变成了>=,进而用最长路求最小值。

bubuko.com,布布扣
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int next[1000000]={0},point[30001]={0},en[1000000]={0},va[1000000]={0},dis[30001]={0},que[1000001]={0};
bool visit[30001]={false};
int main()
{
    int i,j,n,h,b,e,t,tot=0,head,tail,y,x;
    scanf("%d%d",&n,&h);
    for (i=1;i<=h;++i)
    {
        scanf("%d%d%d",&b,&e,&t);
        --b;
        ++tot;
        next[tot]=point[b];
        point[b]=tot;
        en[tot]=e;
        va[tot]=t;
    }
    for (i=1;i<=n;++i)
    {
        ++tot;
        next[tot]=point[i-1];
        point[i-1]=tot;
        en[tot]=i;
        va[tot]=0;
        ++tot;
        next[tot]=point[i];
        point[i]=tot;
        en[tot]=i-1;
        va[tot]=-1;
    }
    memset(dis,128,sizeof(dis));
    dis[0]=0;head=0;tail=1;
    visit[0]=true;que[1]=0;
    do{
        head=head%1000000+1;
        x=que[head];
        visit[x]=false;
        y=point[x];
        while (y!=0)
        {
            if (dis[x]+va[y]>dis[en[y]])
            {
                dis[en[y]]=dis[x]+va[y];
                if (!visit[en[y]])
                {
                    visit[en[y]]=true;
                    tail=tail%1000000+1;
                    que[tail]=en[y];
                }
            }
            y=next[y];
        }
    }while(head!=tail);
    printf("%d\n",dis[n]);
}
View Code

 

 

CODEVS1768种树3

题目描述 Description

为了绿化乡村,H村积极响应号召,开始种树了。

H村里有n幢房屋,这些屋子的排列顺序很有特点,在一条直线上。于是方便起见,我们给它们标上1~n。树就种在房子前面的空地上。

同时,村民们向村长提出了m个意见,每个意见都是按如下格式:希望第li个房子到第ri个房子的房前至少有ci棵树。

因为每个房屋前的空地面积有限,所以每个房屋前最多只能种ki棵树

村长希望在满足村民全部要求的同时,种最少的树以节约资金。请你帮助村长。

思路:和上一题的大致思路一样,不过有一点不同,0<=f[i]-f[i-1]<=k[i],其他都相同。

bubuko.com,布布扣
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int next[5000000]={0},point[500001]={0},en[5000000]={0},va[5000000]={0},k[500001]={0},que[10000001]={0};
long long dis[500001]={0};
bool visit[500001]={false};
int main()
{
    int i,j,n,m,lp,rp,cp,head,tail,tot=0,x,y;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i)
      scanf("%d",&k[i]);
    for (i=1;i<=m;++i)
    {
        scanf("%d%d%d",&lp,&rp,&cp);
        --lp;
        ++tot;
        next[tot]=point[lp];
        point[lp]=tot;
        en[tot]=rp;
        va[tot]=cp;
    }
    for (i=1;i<=n;++i)
    {
        ++tot;
        next[tot]=point[i-1];
        point[i-1]=tot;
        en[tot]=i;
        va[tot]=0;
        ++tot;
        next[tot]=point[i];
        point[i]=tot;
        en[tot]=i-1;
        va[tot]=-k[i];
    }
    head=0;tail=1;que[1]=0;
    memset(dis,128,sizeof(dis));
    dis[0]=0;visit[0]=true;
    do{
        head=head%10000000+1;
        x=que[head];
        visit[x]=false;
        y=point[x];
        while(y!=0)
        {
            if (dis[x]+va[y]>dis[en[y]])
            {
                dis[en[y]]=dis[x]+va[y];
                if (!visit[en[y]])
                {
                    visit[en[y]]=true;
                    tail=tail%10000000+1;
                    que[tail]=en[y];
                }
            }
            y=next[y];
        }
    }while(head!=tail);
    printf("%lld\n",dis[n]);
}
View Code

 

 poj3159Candies

题目大意:已知a、b两名小朋友间的差值关系(b-a<=c),求n-1的最大值。

思路:又变成了赤裸裸的差分约束系统,a到b的边权为c,搜最短路就可以了。但是这个题的数据卡spfa+queue,从网上学习到spfa可以用栈实现,于是就ac了。(太长知识了,从不知道stack可以实现spfa。。。)

 

bubuko.com,布布扣
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int next[3000000]={0},point[300001]={0},en[3000000]={0},va[3000000]={0},que[1000001]={0};
long long dis[300001]={0};
bool visit[300001]={false};
int main()
{
    int i,j,n,m,tot=0,a,b,c,top=0,x,y;
    scanf("%d%d",&n,&m);
    for (i=1;i<=m;++i)
    {
        scanf("%d%d%d",&a,&b,&c);
        ++tot;
        next[tot]=point[a];
        point[a]=tot;
        en[tot]=b;
        va[tot]=c;
    }
    top=1;que[1]=1;visit[1]=true;
    memset(dis,127,sizeof(dis));dis[1]=0;
    while(top>0)
    {
        x=que[top];
        --top;
        visit[x]=false;
        y=point[x];
        while(y!=0)
        {
            if (dis[en[y]]>dis[x]+va[y])
            {
                dis[en[y]]=dis[x]+va[y];
                if (!visit[en[y]])
                {
                    visit[en[y]]=true;
                    ++top;
                    que[top]=en[y];
                }
            }
            y=next[y];
        }
    }
    printf("%lld\n",dis[n]);
}
View Code

 

在今后的学习中可能还会遇到大于或小于的情况,都可以通过所谓的常数(通常是右侧的部分)+1\-1来转化为>=\<=。

通常情况下最好用spfa,还要判断负环,有的话就不存在。

对于不等式的选取和转化可能是一个难点,但真正能找出题目的本意,正确的套用差分约束系统,才是重点和难点。多加练习吧!!!

差分约束系统 初见

原文:http://www.cnblogs.com/Rivendell/p/4098257.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!