题目链接: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;
}