Week8 作业 A - 区间选点 II POJ - 1201 && Gym - 270737F
题目描述:
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点;
输入输出规模及约定:
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。
1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且1 <= ci <= bi - ai+1。
输出一个整数表示最少选取的点的个数
思路:
关于差分约束系统:https://blog.csdn.net/my_sunshine26/article/details/72849441 ,这篇博文写的挺好,不再搬运了。
如果我们用sum[i]表示在i及之前,选了多少了个点,则下述差分约束成立:
sum[bi+1] - sum[a] >= ci
0=<sum[i+1] - sum[i] <=1
然后跑最长路,sum[ max{bi} ]就是结果;
值得注意的点:
1、用sum[b+1] - sum[a] 而不是 sum[b] - sum[a-1] ,因为我们图的顶点通常不用负值。相当于把数轴(所有区间)向右移了一个单位。最后求bi的max的时候,每个bi必须加1
2、同理用sum[i+1] - sum[i] 而不是 sum[i] -sum[i-1]
3、存储边的数组要开的足够大,该结论可以通过仔细分析差分约束条件得出。
代码:
#include <cstdio>
#include <iostream>
#include <queue>
#include <climits> //防止出现负的下标,转换成sum[b+1]-sum[a]>=c
using namespace std;
const int MAXN=5e4+5;
struct e
{
int v,w,next;
}Edge[3*MAXN]; //值得注意的是边数
int last[MAXN]; //因为可能有零点,last数组要初始化成-1
int tot;
int dis[MAXN],inq[MAXN];
int maxb=-1;
int mina=INT_MAX;
void addEdge(int u,int v,int w)
{
tot++;
Edge[tot].v=v;
Edge[tot].w=w;
Edge[tot].next=last[u];
last[u]=tot;
}
void SPFA()
{
for(int i=mina;i<=maxb;i++)
dis[i]=-INT_MAX;
queue<int> q;
q.push(mina);
inq[mina]=1;
dis[mina]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
inq[u]=0;
for(int i=last[u];i!=-1;i=Edge[i].next)
{
int v=Edge[i].v,w=Edge[i].w;
if(dis[v]<dis[u]+w)
{
dis[v]=dis[u]+w;
if(inq[v]==1) continue;
else inq[v]=1,q.push(v);
}
}
}
}
int main()
{
int N; cin>>N;
for(int i=0;i<MAXN;i++) last[i]=-1;
for(int i=1;i<=N;i++)
{
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
maxb=max(v+1,maxb);
mina=min(u,mina);
addEdge(u,v+1,w); //(v)-(u-1)>=c
}
for(int i=mina;i<=maxb;i++)
{
addEdge(i+1,i,-1);
addEdge(i,i+1,0);
}
SPFA();
cout<<dis[maxb]<<endl;
return 0;
}

浙公网安备 33010602011771号