贪心没思路的话,可以试一下排序+举例子。感觉没问题时候尝试证明。
1 区间选点
策略:
证明:
设上述策略答案为cnt。因为按上述这样选,第一个区间一定被选择,接下来一个有点被选择的区间一定在前一个被选择区间右部且无交集,因此目标最小值最少有cnt个选点,因此cnt是答案,上述策略得证。
AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
pair<int,int> a[N];
bool cmp(pair<int,int> a, pair<int,int> b)
{
return a.second < b.second; // 右端点从小到大
}
int main()
{
int n;
cin >> n;
int cnt = 0;
for(int i = 0; i < n; i++)
{
int x,y;
cin >> x >> y;
a[cnt] = make_pair(x,y);
cnt++;
}
sort(a,a+n,cmp);
int ans = 1;
int ed = a[0].second;
for(int i = 1; i < n; i++)
{
if(a[i].first > ed)
{
ed = a[i].second;
ans++;
}
}
cout << ans << endl;
}
2 最大不相交区间数量
策略,证明:和上一题一样。
AC代码:代码也和上一题一样一样的。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
pair<int,int> a[N];
int cnt;
bool cmp(pair<int,int> a,pair<int,int> b)
{
return a.second<b.second;
}
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
int x,y;
cin >> x >> y;
a[cnt++] = make_pair(x,y);
}
sort(a,a+n,cmp);
int ans = 1;
int ed = a[0].second;
for(int i = 1; i < n; i++)
{
if(a[i].first > ed)
{
ed = a[i].second;
ans++;
}
}
cout << ans;
return 0;
}
3 区间分组
题目意思:
策略:
解释:L[I] > Max_r 表示当前区间的左端点要大于任意一个组的区间右端点的最大值(如果有多个这样的组随便一个找个组放进去就行 ?这个点没有证明感觉很玄学)。
证明:因为当前区间的左端点一定大于之前任意一个区间的左端点 而如果存在一个组的右端点的最大值大于当前区间左端点时候,则当前区间和这个组一定是区间重叠的,否则就是区间不重合的,即可以加进去的。
注明:什么时候左端点排序,什么时候右端点排序也是没有统一套路。
语法知识:不能用set迭代器去改一个值 可以先删掉再加入。
edset.erase(it); edset.insert(a[i].second); // *it = a[i].second; //语法知识:不能用set迭代器去改一个值 可以先删掉再加入。
我的AC代码:
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
const int N = 1e5+10;
pair<int,int> a[N];
int cnt;
bool cmp(pair<int,int> a,pair<int,int> b)
{
return a.first<b.first; // 左端点升序
}
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
int x,y;
cin >> x >> y;
a[cnt++] = make_pair(x,y);
}
sort(a,a+n,cmp);
int ans = 1;
int ed = a[0].second;
multiset<int> edset; //要用multiset 不能用set 万一有重复的最大区间右端点的多个分组也要一起考虑不去重
edset.insert(ed);
for(int i = 1; i < n; i++)
{
auto it = edset.begin();
if(a[i].first <= *it) //新开一个组
{
edset.insert(a[i].second);
ans++;
}
else //加入
{
edset.erase(it);
edset.insert(a[i].second);
// *it = a[i].second; //语法知识:不能用set迭代器去改一个值 可以先删掉再加入。
}
}
cout << ans;
return 0;
}
y总版本:
在set那一部分换成了优先队列(priority_queue),查了一下set和priority_queue时间复杂度都是O(logn)。且用了结构体和重载减号(先忽略这个点,用我熟悉的方式也可以实现。
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
int n;
struct Range
{
int l, r;
bool operator< (const Range &W)const
{
return l < W.l;
}
}range[N];
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
int l, r;
scanf("%d%d", &l, &r);
range[i] = {l, r};
}
sort(range, range + n);
priority_queue<int, vector<int>, greater<int>> heap;
for (int i = 0; i < n; i ++ )
{
auto r = range[i];
if (heap.empty() || heap.top() >= r.l) heap.push(r.r);
else
{
heap.pop();
heap.push(r.r);
}
}
printf("%d\n", heap.size());
return 0;
}