POJ 1201 Intervals(差分约束)

273 篇文章 0 订阅
112 篇文章 0 订阅

POJ 1201 Intervals(差分约束)

http://poj.org/problem?id=1201

题意:有n个如下形式的条件:

ai bi ci,表示在区间[ai, bi]内至少要选择ci个整数点.

问你满足n个条件的情况下,最少需要选多少个点?

分析:

       令s[x]表示从区间[0,x]中选择的整数点个数.那么对于条件[ai, bi]选数>=ci个,就是 s[bi]-s[ai-1]>=ci. 即s[ai-1]<=s[bi]+ (-ci)

       假设有下面两个条件:

1 2 3  5 6 3, 那么我们自然得出了s[2]-s[0]>=3 且s[6]-s[4]>=3.

如果s[2]=3,s[0]=0,s[6]=3,s[4]=0 是满足上面不等式的.但是其实是不合题意的.因为s[4]必然要>=s[2]和s[0]的.所以我们需要把s的相对值也联系起来.

       这样就需要添加下面的边. 假设我们令输入读取的区间最大值为max_v. 那么对于所有 0<i<=max_v的值来说,有

       0<=s[i]-s[i-1]<=1 .转换一下即是: s[i]<=1+s[i-1] && s[i-1]<=0+s[i].

       根据上面的分析,我们要建的有向图是一个具有max_v+1个点的图.其中的边有:

       n条s[bi]-s[ai-1]>=ci条件构成的从bi (ai-1)的长-ci的边.

还有所有 0<i<=max_v的值构成的 ii-1的长0的边 i-1i的长1的边.所以这样我们就形成了一个具有点[0,max_v]的有向图.在原图处理时,为了避免ai-1==-1,我们令所有ai与bi都自增了10.所以我们形成了一个具有点[9,max_v]的有向图(其实就是差分约束系统).我们的目标是令S[max_v]-S[9]的值最小.(切记这里不是仅使S[max_v]的值最小,因为我们这里只有从9 ,10,11,…max_v 的值构成了一个差分约束系统.max_v的值和0号点的值是不在一个系统的. 0号点是超级源点,它的值与系统中其他点的值是无关的.)

       然后我们上面已经知道了希望让系统中S[max_v]和S[9]的值差距尽量小.(构成差分约束系统时,1.如果在所有点外添加一个超级源0号点,并使得超级源到所有其他点的距离为0,那么最终求出的0号点到其他所有原始点的最短距离就是本系统的一个可行解,且可行解之间的差距最小.      2.如果初始时不添加超级源,只是将原始点的初始距离设为INF,且令其中一个原始点的初始距离为0,然后求该点到其他所有点的最短距离,那么最短距离的集合就是一个可行解,且该可行解两两之间的差距最大.注意方案2只能在该问题一定存在解的时候即肯定不存在负权环的时候用.否则从1号点到其他点没有路,但是其他点的强连通分量中有负权环,这样根本探测不到错误) 所以我们需要采取方案1.

AC代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn= 50000+10;
const int maxm=500000+10;
#define INF 1e9

struct Edge
{
    int from,to,dist;
    Edge(){}
    Edge(int f,int t,int d):from(f),to(t),dist(d){}
};

struct SPFA
{
    int n,m;
    int head[maxn],next[maxm];
    Edge edges[maxm];
    int d[maxn];
    bool inq[maxn];

    void init()
    {
        m=0;
        memset(head,-1,sizeof(head));
    }

    void AddEdge(int from,int to,int dist)
    {
        edges[m]=Edge(from,to,dist);
        next[m]=head[from];
        head[from]=m++;
    }

    int spfa()
    {
        memset(inq,0,sizeof(inq));
        for(int i=0;i<n;i++) d[i]= i==0?0:INF;
        queue<int> Q;
        Q.push(0);

        while(!Q.empty())
        {
            int u=Q.front(); Q.pop();
            inq[u]=false;

            for(int i=head[u];i!=-1;i=next[i])
            {
                Edge &e=edges[i];
                if(d[e.to]>d[u]+e.dist)
                {
                    d[e.to]=d[u]+e.dist;
                    if(!inq[e.to])
                    {
                        inq[e.to]=true;
                        Q.push(e.to);
                    }
                }
            }
        }
        return d[n-1]-d[9];
    }
}SP;

int main()
{
    int n,max_v=-1;
    scanf("%d",&n);
    SP.init();
    while(n--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        b+=10,a+=10;//所有值都加10了,以免a-1成为-1
        max_v=max(max_v,b);
        SP.AddEdge(b,a-1,-c);
    }
    for(int i=10;i<=max_v;i++)//从该循环可看出,本差分约束的点编号为:[9,max_v](未包含超级源0号点)
    {
        SP.AddEdge(i,i-1,0);
        SP.AddEdge(i-1,i,1);
        SP.AddEdge(0,i,0);
    }
    SP.AddEdge(0,9,0);
    SP.n=max_v+1;
    printf("%d\n",SP.spfa());//注意最终结果是d[max_v]-d[9]的值,而不是d[max_v]的单独值
    return 0;
}

AC代码: 为了验证0号超级源在差分约束系统之外,可以把上面的代码和下面的代码做个对比.

//AC代码2
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn= 50000+10;
const int maxm=500000+10;
#define INF 1e9

struct Edge
{
    int from,to,dist;
    Edge(){}
    Edge(int f,int t,int d):from(f),to(t),dist(d){}
};

struct SPFA
{
    int n,m;
    int head[maxn],next[maxm];
    Edge edges[maxm];
    int d[maxn];
    bool inq[maxn];

    void init()
    {
        m=0;
        memset(head,-1,sizeof(head));
    }

    void AddEdge(int from,int to,int dist)
    {
        edges[m]=Edge(from,to,dist);
        next[m]=head[from];
        head[from]=m++;
    }

    int spfa()
    {
        memset(inq,0,sizeof(inq));
        for(int i=0;i<n;i++) d[i]= i==0?0:INF;
        queue<int> Q;
        Q.push(0);

        while(!Q.empty())
        {
            int u=Q.front(); Q.pop();
            inq[u]=false;

            for(int i=head[u];i!=-1;i=next[i])
            {
                Edge &e=edges[i];
                if(d[e.to]>d[u]+e.dist)
                {
                    d[e.to]=d[u]+e.dist;
                    if(!inq[e.to])
                    {
                        inq[e.to]=true;
                        Q.push(e.to);
                    }
                }
            }
        }
        return d[n-1]-d[3];//修改111111111
    }
}SP;

int main()
{
    int n,max_v=-1;
    scanf("%d",&n);
    SP.init();
    while(n--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        b+=4,a+=4;//所有值都加1了,以免a-1成为-1//修改22222222222
        max_v=max(max_v,b);
        SP.AddEdge(b,a-1,-c);
    }
    for(int i=4;i<=max_v;i++)//从该循环可看出,本差分约束的点编号为:[3,max_v](未包含超级源0号点)//修改333333
    {
        SP.AddEdge(i,i-1,0);
        SP.AddEdge(i-1,i,1);
        SP.AddEdge(0,i,0);
    }
    SP.AddEdge(0,3,0);//修改44444444444
    SP.n=max_v+1;
    printf("%d\n",SP.spfa());//注意最终结果是d[max_v]-d[9]的值,而不是d[max_v]的单独值
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值