区间选点
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点,要求使用差分约束
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
输出一个整数表示最少选取的点的个数
sample input:
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
sample output:
6
思路:
- 因为题目要求使用差分约束,所以需要找到它的约束条件
- 差分约束的每个约束条件是由两个其中的变量做差构成的,比如xi-xj<ck(ck为常数,可正可负)
- 差分约束就是列出所有的约束条件,然后求一组解x1,x2,x3…使得所有的约束条件得到满足,否则判断出无解
- 对于差分约束系统,都可以转换为图论中单源最短路问题,差分约束的每一个不等式约束条件xi-xj<ck都可以通过移相转变为xi<ck+xj,这样就和图论中的dis[i] < dis[j] +w(u,v)可以对应上,对于差分约束的求解其实就可以对应最短路问题中的松弛操作
- 因为我们要保证解的存在性,所以使用SPFA算法来判断负环是否存在是有必要的,具体思路可以参考之前SPFA的例题
- 对于本题,由于需要第i个区间中里至少有ci个点,所以我们假设 i 到数轴起点1放置的点数为dis[i] ,存在dis[bi] - dis[ai-1] >=ci (条件一)
- 后面的点的dis值肯定大于前面的点的dis : dis[a]>=dis[a-1] (条件二)
- 同时需要注意给定的条件,为了使得dis有意义,0<=dis[i+1] - dis[i]<=1 (条件三)
- 最后我们把0视为源点,初始化每个点与后一点之间的边的距离关系,利用spfa来求最长路来求出最长路径的权值和的最小解,最终输出所有区间最右端点max的路径长即可
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
const int N=5e+5;
int maxx;
struct Edge
{
int to,next,w;
}e[N];
int head[N],dis[N],tot;
bool visit[N];
queue<int> q;
void add(int u,int v,int w)
{
e[tot].to=v;
e[tot].next=head[u];
e[tot].w=w;
head[u]=tot;
tot++;
}
void spfa(int a)
{
for(int i = 0; i <= maxx;i++)
dis[i] = -N;
memset(visit,false,sizeof(visit));
q.push(a);
dis[a] = 0;
visit[a] = true;
while(!q.empty())
{
int temp = q.front();
q.pop();
visit[temp] = false;
for(int i = head[temp]; i!=-1; i = e[i].next)
{
int j = e[i].to;
if(dis[j] < dis[temp] + e[i].w)
{
dis[j] = dis[temp] + e[i].w;
if(!visit[j])
{
q.push(j);
visit[j] = true;
}
}
}
}
}
int main()
{
int n;
scanf("%d",&n);
tot=0;
maxx=-1;
memset(head,-1,sizeof(head));
while(n--)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
maxx=max(v,maxx);
add(u,v+1,w);
}
maxx++;
for(int i=0;i<=maxx;++i)
{
add(i,i+1,0);
add(i+1,i,-1);
}
spfa(0);
cout<<dis[maxx];
return 0;
}