【刷题】贪心——区间问题:区间选点

该博客探讨了一个算法问题,涉及在数轴上选择最少的点以确保每个闭区间内至少有一个点。通过排序区间并按特定规则插入点,可以找到最优解。问题的应用场景是广告投放,要求在街道的广告位上放置最少数量的广告,确保每个行人至少看到指定次数的广告。算法首先按右端点排序,然后遍历区间,根据区间长度决定插入广告的位置。此方法在时间复杂度上是可行的。

数轴上有n个闭区间 [ a i , b i ] [a_i,b_i] [ai,bi]。选择尽量少的点,使得每个区间内都至少有一个选择的点。


算法:
将区间按右端点从小到大排序,依次枚举每个区间。
如果区间包含点:跳过
如果区间不包含点:将点放在区间右端点。


正确性:

设算法得到的点的个数是A,最优解得到点的个数是B

A ≥ B A \geq B AB
因为每个区间都包含点,因此A是合法解,最优解显然不超过合法解。

B ≥ A B \geq A BA
按照算法选出来的放点的区间,一定是两两没有交集的,否则这个区间会包含上个区间放的点。
因为这些区间两两没有交集,这些区间数为A,因为每个点至多覆盖1个这种区间,所以至少要放A个点。

因此所有可行方案的解 ≥ A \geq A A,最优解也是可行方案,即最优解 B ≥ A B\geq A BA


UVA10148
题目链接

某条街道上有很多个广告位,某公司在这条街上投放广告,1位置只能投放1个广告。因为不同地方的人流量是不同的,所以公司先调查了N个人,知道了他们每个人每天在街上走的路段。

现在要求找到一些广告位,使得广告位总数量最少,但是要求调查到的那些每人至少看到K次广告。如果有人走的路段广告位少于K个,那么要求他路段上的所有广告位都要有广告。输出要求的广告位的位置。

输入:
第一行输入K和N, ( 1 ≤ K , N ≤ 1000 ) (1 \leq K, N \leq 1000) (1K,N1000)
接下去N行输入区间左端点l和右端点r, ( − 10000 ≤ l , r ≤ 10000 ) (-10000\leq l, r \leq 10000) (10000l,r10000)

输出:
整数M,代表最少的广告数
接下去M行,代表广告的坐标

做法:
先按右端点从小到大排序,再按左端点从大到小排序,这样相同右端点更长的区间会在前面。

依次遍历每个区间,查看区间长度
1、区间长度<=K,从区间左端点遍历到右端点,每个点插上广告,之前已经被插过广告的点跳过。
2、区间长度>K,从区间左端点遍历到右端点计算广告总数,如果大于K则跳过,否则从区间右端点往左端点插广告插到=K为止
情况1其实可以包括在情况2里,只要改下判断:往左端点插广告插到=K并且不超过左端点

因为区间范围较小,所以这样做时间复杂度是2e7,可以接受。

注意两个test之间要有回车隔开,多一个少一个都不行。

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;


int T, K, N, M, ans;
bool flag[20005];
struct Node {
	int l, r;
	 bool operator< (Node p)        //自定义比较函数
    {
		//按照right从小到大排序,right相等按left从大到小排序
        if(r != p.r)	return r < p.r; 
        else	return l > p.l;
    }
};
Node p[1005];

int main() {
	scanf("%d", &T);
	while (T -- ) {
		scanf("%d%d", &K, &N);
		for (int i = 0; i < N; i ++ ) {
			scanf("%d%d", &p[i].l, &p[i].r);
			if (p[i].l > p[i].r) swap(p[i].l, p[i].r);
		}
		sort(p, p + N);
		memset(flag, 0, sizeof flag);
		ans = 0;
		for (int i = 0; i < N; i ++ ) {
			int l = p[i].l + 10000, r = p[i].r + 10000;
			int cnt = 0;
			
			for (int j = l; j <= r; j ++ ) {
				if (flag[j]) cnt ++ ;
			}
			if (cnt < K) {
				int cnt2 = 0;
				for (int j = r; j >= l && cnt + cnt2 < K; j -- ) {
					if (!flag[j]) {
						ans ++ ;
						cnt2 ++ ;
						flag[j] = true;
					}
				}
			}
		}
		printf("%d\n", ans);
		for (int i = 0; i <= 20000; i ++ ) {
			if (flag[i]) printf("%d\n", i - 10000);
		}
		if (T) printf("\n");
	} 
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值