区间选点2

题目描述:

给定一个数轴上的 n n n 个区间,要求在数轴上选取最少的点使得第 i i i个区间 [ a i , b i ] [a_{i}, b_{i}] [ai,bi] 里至少有 c i c_{i} ci 个点。
使用差分约束系统的解法解决这道题

input:
输入第一行一个整数 n n n 表示区间的个数,接下来的 n n n 行,每一行两个用空格隔开的整数 a , b a,b ab 表示区间的左右端点。 1 < = n < = 50000 , 0 < = a i < = b i < = 50000 1 <= n <= 50000, 0 <= a_{i} <= b_{i} <= 50000 1<=n<=500000<=ai<=bi<=50000 并且 1 < = c i < = b i − a i + 1 1 <= c_{i} <= b_{i} - a_{i}+1 1<=ci<=biai+1
output:
输出一个整数表示最少选取的点的个数

样例输入:

5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

样例输出:

6

解题思路:

该题目要求用差分约束系统来解决。在差分约束系统中,所有的不等式都是
x i − x j ≤ T x_{i}-x_{j}\leq T xixjT的形式,将其移项之后为 x i ≤ T + x j x_{i}\leq T+x_{j} xiT+xj,正好对应于图中求解最短路的松弛操作,并且松弛之后取得是等于号,也就是该不等式的上界,最大值,如果将不等式中的 ≥ ≥ 换成 ≤ ≤ ,那么我们就可以跑最长路,这样最后的结果取的是下界,最小值。

对于该题目,我们可以记 s u m [ i ] sum[i] sum[i]为数轴上 [ 0 , i ] [0,i] [0,i]之间选点的个数, s u m [ i ] sum[i] sum[i]就相当于最短路中的 d i s [ i ] dis[i] dis[i]。对于第 i i i个区间 [ a i , b i ] [a_{i},b_{i}] [ai,bi]要求至少有 c i c_{i} ci个点,那么就相当于 s u m [ b i ] − s u m [ a i − 1 ] ≥ c i sum[b_{i}]-sum[a_{i}-1]≥c_{i} sum[bi]sum[ai1]ci,并且题目要求我们求最小解,那么我们可以通过spfa跑一遍最长路,然后输出 s u m [ m a x ( b i ) ] sum[max(b_{i})] sum[max(bi)]。并且我们要保证 s u m sum sum是有意义的,所以我们要满足 0 ≤ s u m [ i ] − s u m [ i − 1 ] ≤ 1 0\leq sum[i]-sum[i-1]\leq 1 0sum[i]sum[i1]1即对于坐标上的 i i i号点我们有可能选也有可能不选。由于是求最小解,我们要将 ≤ ≤ 转变为 ≥ ≥ ,则上面那个式子就被拆分成两个,为 s u m [ i ] − s u m [ i − 1 ] ≥ 0 sum[i]-sum[i-1]≥0 sum[i]sum[i1]0 s u m [ i − 1 ] − s u m [ i ] ≥ − 1 sum[i-1]-sum[i]≥ -1 sum[i1]sum[i]1,所以我们将图构建好之后,为了保证这个条件,我们还要在图里加两条边,一条是以 i − 1 i-1 i1为起点, i i i为终点,权值为 0 0 0的边,另一条是以 i i i为起点, i − 1 i-1 i1为终点,权值为 − 1 -1 1的边。在图构建好之后,然后就可以通过spfa跑一边最长路,得到答案。

在实现方面,这一次我使用了链式前向星,考虑到stl中的vector可能会卡时间,所以就没再使用,在加边的时候,要向图中加入一条 ( a , b + 1 , c ) (a,b+1,c) (a,b+1,c)的边,因为要保证 s u m [ b i ] − s u m [ a i − 1 ] ≥ c i sum[b_{i}]-sum[a_{i}-1]≥c_{i} sum[bi]sum[ai1]ci中间有 b − a + 1 b-a+1 ba+1个点,如果只是添加 ( a , b , c ) (a,b,c) (a,b,c),那么该区间内就只有 b − a b-a ba个点,由于 a − 1 a-1 a1可能会是负值,所以在添加边的时候没有让 a − 1 a-1 a1,而是 b + 1 b+1 b+1

代码:

#include<iostream>
#include<stdio.h>
#include<queue>
using namespace std;
struct edge{//边结构体,链式前向星
	int st,ed,wei,next;
	edge(){
		
	}
}; 
int head[50010];
edge ed[150010];//边数组
int sum[50010];
int inc[50010];
int total,n;
void add(int u,int v,int w)
{//加边
	ed[total].st=u;
	ed[total].ed=v;
	ed[total].wei=w;
	ed[total].next=head[u];
	head[u]=total;
	total++;
}
int main()
{
	total=0;
	for(int i=0;i<50010;i++)
	{//初始化
		head[i]=-1;
		sum[i]=-1000000;
		inc[i]=0;
	}
	cin>>n;
	int maxb=0;
	for(int i=0;i<n;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b+1,c);//向图中加边
		if(maxb<b+1)//始终将右端点的最大值保存在maxb
			maxb=b+1;
	}
	for(int i=1;i<=maxb;i++)
	{//为了保证sum有意义,加两条边
		add(i-1,i,0);
		add(i,i-1,-1);
	}
	sum[0]=0;//spfa
	queue<int> qq;
	qq.push(0);
	inc[0]=1;
	while(!qq.empty())
	{
		int temp=qq.front();
		qq.pop();
		inc[temp]=0;
		for(int i=head[temp];i!=-1;i=ed[i].next)
		{
			int end=ed[i].ed;
			if(sum[end]<sum[temp]+ed[i].wei)
			{
				sum[end]=sum[temp]+ed[i].wei;
				if(inc[end]!=1)
				{
					qq.push(end);
					inc[end]=1;
				}
			}
		}
	}
	cout<<sum[maxb]<<endl;//输出sum[maxb]
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值