给出n个在x轴上的区间,记第i个区间的左端点和右端点为(l_i,r_i),现在有n种点(显然都是在x轴上),第i种点的坐标都是(p_i),数量有(c_i),每个点只可以标记包含它的一个区间,问最多可以标记多少个区间。(c,lleq 2500)。
解
注意到这是区间问题,首要考虑排序,而数据范围是给(O(n^2)),选择点时应该会有暴力枚举的过程。
思路一:
按左端点排序,然后再来考虑对于一个区间来说选择标记它的点的策略,按照最朴素的思想,显然想选择可以标记这个区间i的点中,坐标最小的点,不妨考虑微扰证明,那么对于后面的区间,对于可以标记区间i的任意两个点(x,y,(x<y))而言,因为后面的区间左端点必然是递增的,显然后面的区间对于它们只有四种情况,一是x可选,y可选,二是x可选,y不可选,三是x不可选,y可选,四是x不可选,y不可选;如果除去第二种情况,那么选择坐标最小的,两者都可选,让i选最小的没问题,后者不可选,自然i必须选前者还是接下来的区间选前者,两者都不可选,自然i选前者选后者都没有关系,但是前者可选,后者不可选,选择前者可能导致后面不优秀,于是此思路萎掉。
思路二:
注意到排序方式有两种,而且会造成思路大不同,从这里作为突破点。
于是按右端点排序,然后接着考虑一个区间i的点的选择的策略情况,参考朴素,想到点的坐标选的尽可能小,此时对于两个点(x,y(x<y))而言,后面的区间j右端点必然是递增的,只需要考虑左端点,判断是否包含这些点,如果左端点(r_j>y),那么x,y对于j都不可选,如果(r_i<xleq r_j),x不可选,y可选,如果(r_ileq x),两者都可选,同思路一的证明方法,容易知道,这三种情况,按照这种朴素贪心都是正确的,再对比一下,容易知道,思路二的情况比思路一少了一种,也就是恶心的x可选,y不可选。
但是还没完,这只是证明一个区间应该选择那些点,并没有证明,这个区间是否一定要选点,这样考虑,如果这个区间放弃这个点,让别的没有被选的区间选了,答案-1+1,不改变,如果它是让一个已经选的区间选了,腾出来另外一个点给别的区间,答案也是-1-1+1+1,还是没有改变,于是这个区间不选点不会让后面的结果更优,不妨选点。
到此,题目就结束了,但各位必然深有感触,我们只要排个序,暴力扫描应该选哪一个点,就可以(O(n^2))解决这到题目了,这也照应了开头所讲,会存在暴力枚举选择哪个点的过程。
区间问题的排序,对于先排左端点还是先排右端点,一定要灵活运用,对于微扰法的证明这样选择结果后面不会更优,一定要全面地考虑所有情况,而且本题存在两个决策,一是一个区间对点的决策,一个是区间的决策,应该思维全面,不要漏掉,这道题很好地考察了贪心的思维。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define il inline
#define ri register
#define intmax 0x7fffffff
using namespace std;
struct inter{int l,r;
il bool operator<(const inter&x)
const{return r<x.r;}
}H[3000],I[3000];
il void read(int&);
int main(){
int c,l,ans(0);read(c),read(l);
for(int i(1);i<=c;++i)read(I[i].l),read(I[i].r);
for(int i(1);i<=l;++i)read(H[i].l),read(H[i].r);
sort(I+1,I+c+1),H[0].l=intmax;
for(int i(1),j,k(0);i<=c;++i){
for(j=1;j<=l;++j)
if(I[i].l<=H[j].l&&H[j].l<=I[i].r
&&H[j].r&&H[j].l<H[k].l)k=j;
if(k)--H[k].r,++ans,k^=k;
}printf("%d",ans);
return 0;
}
il void read(int &x){
x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}