poj1201(差分约束)

题目链接:http://poj.org/problem?id=1201

题意:给定n个区间,[ai,bi]这个区间至少选选出ci个整数,求一个集合z,满足每个区间的要求,输出集合z的大小。
分析:令d[i]表示0到i这个区间内至少要选出d[i]个数,那么对于每个[ai,bi],有d[b] - d[ai-1] >= ci,同时隐含的一个条件是0 <= d[i] - d[i-1] <= 1,令d[i+1]表示起点到i这个区间内至少要选出d[i+1]个数,然后d[起点] = 0,直接求取最长路就行了。边的存储使用链式向前星,这样效率最高。

代码:

#include <iostream>
#include <queue>
#include <stdio.h>
#include <string.h>
#include <string>
#include <math.h>
#include <algorithm>
#include <stdlib.h>

using namespace std;

const int maxn=50000+50;
const int inf=-0x3f3f3f3f;

int head[maxn],cnt,d[maxn],min_,max_,vis[maxn];

struct Edge
{
    int u,v,w,next;
    Edge(){};
    Edge(int _u,int _v,int _w,int _next)
    {
        u=_u,v=_v,w=_w,next=_next;
    }
}edge[maxn*3];//至少要三倍才行

void addedge(int u,int v, int w)
{
    edge[cnt]=Edge(u,v,w,head[u]);
    head[u]=cnt++;
}

int spfa()
{
    d[min_]=0;
    queue<int>q;
    q.push(min_);
    vis[min_]=1;//在队列里面
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        vis[t]=0;
        for(int i=head[t];i!=-1;i=edge[i].next)
        {
            int x=edge[i].v;
            if(d[x]<d[t]+edge[i].w)
            {
                d[x]=d[t]+edge[i].w;
                if(!vis[x])
                    vis[x]=1,q.push(x);
            }
        }
    }
    return d[max_];
}

int main()
{
    int n,a,b,c;
    min_=maxn,max_=-1;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(d,inf,sizeof(d));
    cnt=0;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
    {
        scanf("%d %d %d",&a,&b,&c);
        addedge(a,b+1,c);//假如a=b的话,a到它自己有一条路径,会是一个死循环,所以我就令b+1; 
        min_=min(min_,a);
        max_=max(max_,b);
    }
    max_++;
    for(int i=min_; i<max_; i++)
    {
        addedge(i,i+1,0);
        addedge(i+1,i,-1);
    }
    printf("%d\n",spfa());
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36300700/article/details/80242354