给出n个区间,第i个区间表示为([l_i,r_i]),询问把这些最少的分组数,使得每组内每个区间相离,(1 <= N <= 50,000)。
解
区间问题,首要考虑排序,因为涉及分组,于是得保存组的信息,不妨设(a_i)为分的第i组的最后一个区间的右端点
思路一:按左端点排序
现在来考虑第i个区间的决策点,对于能分进的组j,必然是(a_j<l_j),第i个区间才能被分进去,但是选哪一个组,按照朴素的思想,必然是选(a_j)最小的或者最大的,因为是按左端点排序,后面的区间左端点必然增大,于是前面区间能分进的组,后面的区间也能分进,于是无论选哪一个组都无所谓,如果不能选,必然要新开一个组。
现在的问题是如果能分进一个组,会不会开一个新组让结果更加优秀,此时微扰法就不好证明了,于是采取决策包容,注意到新开一个组,它能够选择的范围只有第i个区间所包含的位置,而不新开一个组,以后新开一个组,与它同等优秀,但是组的最后一个区间的右端点可以是任意一个区间的右端点,于是后者决策包含前者决策,选后者一定不会差,得证。
思路二:按右端点排序
同样考虑第i个区间的决策点,从已经分的组里面考虑,对于第i个区间能被分进的组j,必然(a_j<l_i),那么对于后面的区间右端点是递增的,而左端点的位置不确定,因此i可以选的,j也可以选,能接下来的决策造成了影响,不满足无后效性原则。
总上,我们就只要枚举每一个区间,已经每一个可以分进的组,随便选择一个,而不能分进已存在的组,就新开一个组,这样就可以(O(n^2))解决这个问题了。
但是太慢了,考虑优化,按照特殊化简单化问题的思想,我们只要随便选择一个可以分进的组,不妨选个最特殊的(a_i)最小的组,而且如果它都不能满足条件,所有已经存在的组都不能满足条件,这样问题就变成快速寻找数列({a_i})的最小值,并且支持修改,st表萎的(不支持动态修改),线段树树状数组可以,只要你愿意写,但是优先队列才是最简单的,因此就可(O(nlogn))解决问题。
顺便给重载(greater(),less())函数的人提个醒,就是(greater())用的是大于号,后者用的是小于号。
参考代码:
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <functional>
#include <algorithm>
#define il inline
#define ri register
#define Size 50050
#define intmax 0x7fffffff
using namespace std;
struct inter{
int l,r,id;
il bool operator<(const inter &x)const{
return l<x.l;
}
il bool operator>(const inter &x)const{
return l>x.l;
}
}I[Size],t;int ans[Size];
priority_queue<inter,vector<inter>,greater<inter> >gzy;
il void read(int&);
int main(){int n;read(n);
for(int i(1);i<=n;++i)
read(I[i].l),read(I[i].r),I[i].id=i;
sort(I+1,I+n+1),gzy.push((inter){-intmax,1});
for(int i(1);i<=n;++i){t=gzy.top();
if(t.l<I[i].l)ans[I[i].id]=t.r,t.l=I[i].r,gzy.pop(),gzy.push(t);
else ans[I[i].id]=gzy.size()+1,gzy.push((inter){I[i].r,gzy.size()+1});
}printf("%lu
",gzy.size());
for(int i(1);i<=n;++i)printf("%d
",ans[i]);
return 0;
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
附:
提供一种傻逼做法,也就是傻逼作者提供的,他菜鸡地乱类比xor游戏这到题目,数列中任何一个数可以随便选择一个数让他的值变为两数异或值,求最后数列的和的最大值,那题把每个数尽可能最大化(这个是可以线性基做的),然后就是最优方案,证明他现在都不知道,于是他认为该题一次一次分组,每次分组的元素最多,最终就可以是最优解,事实证明他萎了,现在提供他的垃圾代码供各位神奇嘲笑,引以为戒。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define il inline
#define ri register
#define Size 50050
using namespace std;
struct inter{
int l,r;
il bool operator<(const inter&x){
return r<x.r;
}
}I[Size];
bool check[Size];
int ans[Size],tot,zt;
il void read(int&);
int main(){
int n;read(n);
for(int i(1);i<=n;++i)
read(I[i].l),read(I[i].r);
sort(I+1,I+n+1);
while(tot<n){++zt;
for(int i(1),j(0);i<=n;++i){if(check[i])continue;
if(I[i].l>j)
j=I[i].r,check[i]|=true,ans[i]=zt,++tot;
}
}printf("%d
",zt);
for(int i(1);i<=n;++i)
printf("%d
",ans[i]);
return 0;
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}