题目描述:
给定一个数轴上的 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; }