差分约束系统

   用一下百度搜索 差分约束 得到的解释

   如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。

   求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。

   差分约束系统的求解要利用单源最短路径问题中的三角形不等式,即对有向图(或无向图)中的任何一条边<u,v>都有  d(v)<=d(u)+edge[u][v]   其中:d(u)和d(v)是求得的从源点分别到顶点u和顶点v的最短路径长度 edge[u][v]是边<u,v>的权值

   如果有向网中存在负权值回路 则对应的差分约束系统就无解


我个人的感觉的是首先从题目中找出不等式 写成xi-xj<ak的形式  然后根据这个形式构图 <j,i>边的权值为ak

然后根据源点   求出相应的值(最小值)有的还需要判断是否有解!!


zoj 2770

给出每个兵营的容量 以及第i个兵营到第j个兵营拥有的最少的士兵数 

求第1个到第n个兵营最少的士兵总和

设前i个兵营士兵总和为Si  期中S0=0

第i个到第j个兵营 士兵总数至少有k个  则Sj-Si>=k   则Si-Sj<=-k

第i个到第j个兵营 士兵总数和不会超过这些兵营士兵容量的和 由此也可以得到一些不等式

每个兵营的士兵数不会超过容量  Si-S(i-1)<=Ci

每个兵营的士兵数>=0  Si-S(i-1)>=0   S(i-1)-Si<=0

分别根据这些不等式建图 

然后求路径最小值

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  1010
#define INF 9999999
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int  n,m;//n个兵营  m条消息(m行数据)
int c[MAXN];//兵营的容量  最多能容纳的士兵数量
int dist[MAXN];//从源点到各顶点的最短距离  Sn为源点
int d[MAXN];//前i个兵营容量和
int ei;//边的序号

struct edge
{
    int u,v,w;//边的起点  终点  权值
};

edge e[25010];

void init()
{
    ei=0;
    for(int i=0;i<n;i++)
        dist[i]=INF;
    dist[n]=0;
    d[0]=0;
}

bool Bellman_ford()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<ei;j++)
        {
            int t=dist[e[j].u]+e[j].w;
            if(dist[e[j].u]!=INF&&t<dist[e[j].v])
                dist[e[j].v]=t;
        }
    }
    //检查  若还有更新说明存在无限循环的负值回路
    for(int i=0;i<ei;i++)
    {
        if(dist[e[i].u]!=INF&&dist[e[i].u]+e[i].w<dist[e[i].v])
            return 0;
    }
    return 1;
}

int main()
{
//freopen("ceshi.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int u,v,w;
        init();
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&c[i]);
            e[ei].u=i-1;
            e[ei].v=i;
            e[ei].w=c[i];
            ei++;
            e[ei].u=i;
            e[ei].v=i-1;
            e[ei].w=0;
            ei++;
            d[i]=d[i-1]+c[i];
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            e[ei].u=v;
            e[ei].v=u-1;
            e[ei].w=-w;
            ei++;
            e[ei].u=u-1;
            e[ei].v=v;
            e[ei].w=d[v]-d[u-1];
            ei++;
        }
        if(Bellman_ford())
            printf("%d\n",dist[n]-dist[0]);
        else
            printf("Bad Estimations\n");
    }
    return 0;
}
</span>


zoj 1508 poj 1201 Intervals

有一个整数集合Z  给定n个整数闭区间[ai,bi]  Z集合有第i个集合相交至少有ci个元素

求Z集合中元素最少的个数

设S[i]为Z集合中 小于等于i的元素个数  则 S[bi]-S[ai-1]>=ci

即S[ai-1]-S[bi]<=-ci

根据实际情况还有两个约束条件 S[i]-S[i-1]<=1  S[i-1]-S[i]<=0

如果把这些约束条件都建成边 便有3*50000条边  并不是一个好方法

S[i] <= S[i-1] + 1 等效于 S[i] – S[mx] <= S[i-1] – S[mx] + 1。
假设dist[i]为源点mx 到顶点Si 的最短路径,那么S[i] – S[mx]就是dist[i],S[i-1] – S[mx] + 1
就是dist[i-1] + 1,即如果顶点Si 到源点的最短路径长度大于Si-1 到源点的最短路径长度加1,则
修改dist[i]为dist[i-1] + 1。

S[i-1]<=S[i] 等效于 S[i-1] – S[mx] <= S[i] – S[mx]。
S[i] – S[mx]就是dist[i],S[i-1] – S[mx]就是dist[i-1],即如果顶点Si-1 到源点的最短路径长度
大于Si 到源点的最短路径,则修改dist[i-1]为dist[i]。 

  

所以只有一步是用来建边的 且边的权值为负   根据约束条件 及时修改S[i]的值

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  50100
#define INF 100000
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int mn,mx;//区间最小值 最大值
int dist[MAXN];
int n;//区间个数

struct edge
{
    int u,v,w;
};
edge e[MAXN];

void init()
{
    MEM(dist,0);
    mx=0; mn=INF;
}


void Bellman_ford()
{
    int flag=1;
    while(flag)
    {
        flag=0;
        for(int i=0;i<n;i++)
        {
            int t=dist[e[i].u]+e[i].w;
            if(dist[e[i].v]>t)
            {
                dist[e[i].v]=t;
                flag=1;
            }
        }
        //根据s[i]<=s[i-1]+1  修改s[i]的值
        for(int i=mn;i<=mx;i++)
        {
            int t=dist[i-1]+1;
            if(dist[i]>t)
            {
                dist[i]=t;
                flag=1;
            }
        }
        for(int i=mx;i>=mn;i--)
        {
            int t=dist[i];
            if(dist[i-1]>t)
            {
                dist[i-1]=t;
                flag=1;
            }
        }
    }
}

int main()
{
//freopen("ceshi.txt","r",stdin);
    while(scanf("%d",&n)!=EOF)
    {
        //<v,u-1>权值-w
        init();
        int u,v,w;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            e[i].u=v;
            e[i].v=u-1;
            e[i].w=-w;
            if(v>mx)  mx=v;
            if(u<mn)  mn=u;
        }
        Bellman_ford();
        printf("%d\n",dist[mx]-dist[mn-1]);
    }
    return 0;
}
</span>


poj 3169 Layout

给母牛安排位置  有两种限制 

(1)ML个约束  两个母牛之间能够分隔开的最大距离

(2)MD个约束  两个母牛之间必须分隔开的最小距离

求第1头母牛与第N头母牛之间距离的最大值

针对 ML约束 Si Sj Dk   Sj-Si<=Dk  <i,j>权值为Dk

针对 MD约束 Si Sj Dk   Sj-Si>=Dk  即Si-Sj<=-Dk  <j,i>权值为-Dk

按着这些约束建边就可以  求路径最小值  如果不存在满足条件的排列就输出-1 即存在负环

第i头与第n头母牛直接的距离可以任意  输出-2  dist[n]=INF

否则直接输出最大距离

以第一个位置作为源点建图  约束条件相对简单 建图就比较容易了  

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  20010
#define INF 0x7fffffff
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int n,ml,md;
ll dist[1010];
int cnt;

struct edge
{
    int u,v;
    ll w;
};
edge e[MAXN];

void init()
{
    for(int i=0;i<=n;i++)
        dist[i]=INF;
    dist[1]=0;
    cnt=0;
}

bool Bellman_ford()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<cnt;j++)
        {
            ll t=dist[e[j].u]+e[j].w;
            if(dist[e[j].u]!=INF&&t<dist[e[j].v])
                dist[e[j].v]=t;
        }
    }
    for(int i=0;i<cnt;i++)
        if(dist[e[i].u]!=INF&&dist[e[i].u]+e[i].w<dist[e[i].v])
            return 0;
    return 1;
}

int main()
{
//freopen("ceshi.txt","r",stdin);
    while(scanf("%d%d%d",&n,&ml,&md)!=EOF)
    {
        init();
        for(int i=0;i<ml;i++)
        {
            int u,v;
            ll d;
            scanf("%d%d%I64d",&u,&v,&d);
            e[cnt].u=u; e[cnt].v=v;  e[cnt].w=d;
            cnt++;
        }
        for(int i=0;i<md;i++)
        {
            int u,v;
            ll d;
            scanf("%d%d%I64d",&u,&v,&d);
            e[cnt].u=v; e[cnt].v=u;  e[cnt].w=-d;
            cnt++;
        }
        int flag=Bellman_ford();
//        for(int i=0;i<=n;i++)
//            cout<<dist[i]<<"  ";
//        cout<<endl;
        if(!flag)
            printf("-1\n");
        else
        {
            if(dist[n]==INF)
                printf("-2\n");
            else printf("%I64d\n",dist[n]-dist[1]);
        }
    }
    return 0;
}


zoj 1455 Schedule Problem

完成一个项目 把一个项目分成若干部分  每个部门都需要一定的时间去完成 

各个部分之间有一定的约束条件 

FAS: a,b

S[a]+time[a]>=S[b]

FAF: a,b

S[a]+time[a]>=S[b]+time[b]

SAF: a,b

S[a]>=S[b]+time[b]

SAS: a,b

S[a]>=S[b]

以0作为源点建图   但是在建图方式上有点问题 我一开始建图的方法跑不出正确答案  

第一种方法:

就是我之前想的方式 建图  <a,b>为边建边  但是0与个点建边的权值为-ti[i]

且最后要找出 dist中最小值  最后输出的值 为dist[i]与这个最小值的差

第二种方法:

在根据约束条件建边之后 还要加上 每个点与源点之间的边 边的权值为0

但是在写Bellman_ford()时  要将大小判断改变  dist[e[i].v]要取大值


第一种方法代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100
#define INF 1000000
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int n,cnt;
int ti[10010];
int dist[10010];
int s;

struct edge
{
    int u,v,w;
};
edge e[MAXN];

void init()
{
    for(int i=1;i<=n;i++)
        dist[i]=INF;
    dist[0]=0;
    cnt=0;
}

void addedge(int a,int b,int w)
{
    e[cnt].u=a;
    e[cnt].v=b;
    e[cnt].w=w;
    cnt++;
}

bool Bellman_ford()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<cnt;j++)
        {
            int t=dist[e[j].u]+e[j].w;
            if(t<dist[e[j].v])
                dist[e[j].v]=t;
        }
    }
    for(int i=0;i<cnt;i++)
        if(dist[e[i].u]+e[i].w<dist[e[i].v])
            return 0;
    int mi=INF;
    for(int i=1;i<=n;i++)
    {
        if(mi>dist[i])
        {
            mi=dist[i];
            s=i;
        }
    }
    return 1;
}

int main()
{
//freopen("ceshi.txt","r",stdin);
    int cs=1;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)  break;
        init();
        for(int i=1;i<=n;i++)
            scanf("%d",&ti[i]);
        getchar();
        char ch[10];
        while(1)
        {
            scanf("%s",ch);
            if(ch[0]=='#')
                break;
            int a,b;
            scanf("%d%d",&a,&b);
            if(strcmp(ch,"FAS")==0)
            {
                addedge(a,b,ti[a]);
//                addedge(b,a,-ti[a]);
            }
            if(strcmp(ch,"FAF")==0)
            {
                addedge(a,b,ti[a]-ti[b]);
//                addedge(b,a,ti[b]-ti[a]);
            }
            if(strcmp(ch,"SAF")==0)
            {
                addedge(a,b,-ti[b]);
//                addedge(b,a,ti[b]);
            }
            if(strcmp(ch,"SAS")==0)
            {
                addedge(a,b,0);
//                addedge(b,a,0);
            }
        }
        for(int i=1;i<=n;i++)
            addedge(0,i,-ti[i]);
        int flag=Bellman_ford();
        printf("Case %d:\n",cs++);
        if(!flag)
            puts("impossible");
        else
        {
            for(int i=1;i<=n;i++)
                printf("%d %d\n",i,dist[i]-dist[s]);
        }
        puts("");
    }
    return 0;
}


第二种方法代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100
#define INF 1000000
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int n,cnt;
int ti[10010];
int dist[10010];
int s;

struct edge
{
    int u,v,w;
};
edge e[MAXN];

void init()
{
    for(int i=1;i<=n;i++)
        dist[i]=-INF;
    dist[0]=0;
    cnt=0;
}

void addedge(int a,int b,int w)
{
    e[cnt].u=a;
    e[cnt].v=b;
    e[cnt].w=w;
    cnt++;
}

bool Bellman_ford()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<cnt;j++)
        {
            int t=dist[e[j].u]+e[j].w;
            if(t>dist[e[j].v])
                dist[e[j].v]=t;
        }
    }
    for(int i=0;i<cnt;i++)
        if(dist[e[i].u]+e[i].w>dist[e[i].v])
            return 0;
    return 1;
}

int main()
{
//freopen("ceshi.txt","r",stdin);
    int cs=1;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)  break;
        init();
        for(int i=1;i<=n;i++)
            scanf("%d",&ti[i]);
        getchar();
        char ch[10];
        while(1)
        {
            scanf("%s",ch);
            if(ch[0]=='#')
                break;
            int a,b;
            scanf("%d%d",&a,&b);
            if(strcmp(ch,"FAS")==0)
            {
//                addedge(a,b,time[a]);
                addedge(b,a,-ti[a]);
            }
            if(strcmp(ch,"FAF")==0)
            {
//                addedge(a,b,time[a]-time[b]);
                addedge(b,a,ti[b]-ti[a]);
            }
            if(strcmp(ch,"SAF")==0)
            {
//                addedge(a,b,-time[b]);
                addedge(b,a,ti[b]);
            }
            if(strcmp(ch,"SAS")==0)
            {
                addedge(b,a,0);
            }
        }
        for(int i=1;i<=n;i++)
            addedge(0,i,0);
        int flag=Bellman_ford();
        printf("Case %d:\n",cs++);
        if(!flag)
            puts("impossible");
        else
        {
            for(int i=1;i<=n;i++)
                printf("%d %d\n",i,dist[i]);
        }
        puts("");
    }
    return 0;
}



//差分约束系统  要从题目中找出相应的约束条件  构造合理的序列  有时需要根据现实情况 添加相应的约束条件

比如 在整数的情况下  序列和 前后项之间的一些不等关系  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值