程序设计思维 C - 区间覆盖(贪心算法)

题目

描述
数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]( 1<=t<=1,000,000)。
覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。
不可能办到输出-1

输入
第一行:N和T
第二行至N+1行: 每一行一个闭区间。

输出
选择的区间的数目,不可能办到输出-1

样例输入
3 10
1 7
3 6
6 10

样例输出
2

思路

这道题与之前的B题有点类似,都与区间有关、都可以用贪心算法解决;但又很不一样,从题目就可以得知,这道题的目的是在输入的区间中选出最少的区间以覆盖[1,T]上的整数点(1,2,…,T)。
为了选择最少的区间同时又覆盖所有的整数点,于B题类似,我们需要对区间进行排序,不过这次是按区间的覆盖范围进行排序:
① 左端点的值越小越往前排
② 左端点相同时,右端点的值越大越往前排(覆盖范围越广)
例如,输入区间:
在这里插入图片描述
排序后:
在这里插入图片描述
然后开始选择区间。第一轮,先选择左端点为1(或包含1)且覆盖范围最大的区间,然后将该区间的右端点的值+1作为新的起点。第二轮,选择左端点为新的起点(或包含新的起点)且覆盖范围最大的区间,再将该区间的右端点的值+1作为新的起点······如此循环下去,直至某一轮结束后,新的起点大于T,此时说明已选择的区间已经将[1,T]中的整数点覆盖完了。其中,区间的覆盖范围 = 右端点 - 新的起点。
如图:
在这里插入图片描述
为了加快排序,我使用了STL中的优先队列,因为优先队列相当于栈,排序的时间复杂度为O(NlogN)。同时,当从队列中取出某个区间,且该区间的整体都在新起点的左端时,说明该区间上的点都已经被覆盖了,则可以舍弃该区间,以减少区间的数量和排序时间。
需要注意的是,在给区间排序后,若取出的第一个区间不包含1,则说明无法覆盖到1,即可提前结束退出;当新的起点 < T且再也无法选择下一个能覆盖的区间时,说明所给区间不足以覆盖所有的整数点,也可以结束退出。

代码

#include <iostream>
#include <queue>
#include <cstdio>

using namespace std;

struct range {
	int left, right;
	range(int theLeft, int theRight) { left = theLeft; right = theRight; }
	bool operator < (const range & r)
	{
		if (left != r.left) return left > r.left;
		else return right < r.right;
	}
};

struct cmp {
	bool operator()(range r1, range r2)
	{
		if (r1.left != r2.left) return r1.left > r2.left;
		else return r1.right < r2.right;
	}
};

int n, t;
priority_queue<range, vector<range>, cmp> q;
int next_start;
int start;
bool found;
int cnt;

int main()
{
	while (cin >> n >> t) {\
		next_start = 1;
		start = 1;
		found = false;
		cnt = 0;

		for (int i = 0; i < n; i++) {
			int a, b;
			cin >> a >> b;
			if (b<1 || a>t) continue;
			q.push(range(a, b));
		}

		if (q.top().left > 1) {
			cout << -1 << endl;
			continue;
		}

		while (!q.empty() && start <= t) {
			while (!q.empty()) {
				range p = q.top();
				q.pop();
				int a = p.left, b = p.right;
				if (b < start) continue;
				else if (a <= start) {
					if (next_start <= b) {
						next_start = b + 1;
						found = true;
					}
				}
				else if (a > start&& found) {
					q.push(range(a, b));
					break;
				}
				else if (a > start && !found) goto over;
			}
			start = next_start;
			cnt++;
			found = false;
		}
over:
		if (start <= t) cout << -1 << endl;
		else cout << cnt << endl;
	}
	
}

发布了12 篇原创文章 · 获赞 0 · 访问量 126

猜你喜欢

转载自blog.csdn.net/weixin_43826681/article/details/104851033