贪心算法: 区间选点
c++
区间选点
发现了一个比较有趣的事情
而且 0xcfcfcfcf 直接使用被认为是 unsigned int,并且达不到 -1e9,真的是酸Q,因为会直接 WA
int x = 0xcfcfcfcf; // -808464433
cout << x << endl;
cout << 0xcfcfcfcf << endl; // 3486502863
/*
区间选点
题目描述:
题目搬运:
给定 N 个闭区间 [ai, bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
输入格式:
第一行包含整数 N,表示区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
输出格式:
输出一个整数,表示所需的点的最小数量。
数据范围:
1 ≤ N ≤ 10^5,
−10^9 ≤ ai ≤ bi ≤ 10^9
解题思路1:
本题是一个非常典型的贪心问题,关键是如何寻找正确的贪心思路。
1. 首先,先对区间升序排序,左端点作为第一关键字,右端点作为第二关键字,然后我们思索如何贪心
2. 数组排序之后,我们需要明确一个目标(贪心),在不漏下左边任何一个区间的前提下,将第一个点尽可能的向右
因为,左面不存在遗漏区间时,点越向右越可能覆盖更多的区间。
但是如何实现在 不漏下左边任何一个区间的前提下,将选点尽可能的靠右
在遍历已经排序的区间 segments 时,在当前已选择的 点 point 上更新, min(point, segments[i].right)
而且当 point > segments[i].left, 说明以后的区间 都不会和 point 有交集了,而且此轮的 Point 也一定不会被更新了,
因为 segments[i].left <= sigments[i].right
3. 重复执行 2 操作,直到区间便利完毕。
那么该种方法为什么是正确的,采用归纳的思想证明:
起初只有一个区间时候,选取他的右端点,着一定是正确的。
那么按照这种策略前 i - 1 个区间加入时,都是正确的,现在我们加入第 i 个区间,分以下几种情况:
1. segments[i].left_point <= cur_point,我们更新 cur_point = min(cur_point, segments[i].right_point),
没有增加额外的点,是正确的,而且尽可能的靠右了
2. segments[i].left_point > cur_point,我们新加入一个点,并 cur_point = segments[i].right_point,
而且 segment i 一定不与上面那个最小的区间相交,所以说加这个 point 是必要的,而且我们还尽可能的靠右了。
解题思路2:
1. 首先,先对区间升序排序,右端点作为第一关键字,左端点作为第二关键字,然后我们思索如何贪心
2. 数组排序之后,我们需要明确一个目标(贪心),在不漏下左边任何一个区间的前提下,将第一个点尽可能的向右
因此,我们要求当前 point 取第一个,然后查看其他区间是否被 point 覆盖住,没有覆盖就加入新的 point
因为没有覆盖,表明没有交集,加入一个新的 point 也是无可厚非的。
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
typedef pair<int, int> PII;
const int N = 100010, _INF = -2e9, INF = 0x3f3f3f3f;
PII segments[N];
int n;
bool cmp1(PII t1, PII t2) {
if (t1.first == t2.first) {
return t1.second < t2.second;
} else {
return t1.first < t2.first;
}
}
bool cmp2(PII t1, PII t2) {
if (t1.second == t2.second) {
return t1.first < t2.first;
} else {
return t1.second < t2.second;
}
}
int solution_one() {
// sort and greedy algorithm
int cur_min = _INF;
sort(segments + 1, segments + n + 1, cmp1);
int cnt = 0;
for (int i = 1; i <= n; i ++ ) {
if (cur_min == _INF) {
cur_min = segments[i].second;
} else if (segments[i].first > cur_min) {
cnt += 1;
cur_min = segments[i].second;
} else {
cur_min = min(cur_min, segments[i].second);
}
}
// 补上去最后一个
cnt += 1;
return cnt;
}
int solution_two() {
int cur_point = _INF;
sort(segments + 1, segments + n + 1, cmp2);
int cnt = 0;
for (int i = 1; i <= n; i ++ ) {
if (cur_point < segments[i].first) {
cnt += 1;
cur_point = segments[i].second;
}
}
return cnt;
}
int main()
{
int x = 0xcfcfcfcf; // -808464433
cout << x << endl;
cout << 0xcfcfcfcf << endl; // 3486502863
// input
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) {
scanf("%d%d", &segments[i].first, &segments[i].second);
}
// solution
int res = solution_two();
// output
printf("%d\n", res);
return 0;
}