二分枚举答案,并考虑如下贪心:
从左到右依次选择区间,并维护未选区间的"右边界"
记当前位置为$l$,右边界$\le r$的未选区间数为$cnt_{l,r}$(其中$r\in [l,n]$)
取最小的$r$满足$cnt_{l,r}=|[l,r]|$,将这$cnt_{l,r}$个区间中右端点最小的填在$l$上
关于上述贪心的正确性,证明如下:
将所有区间按右端点从小到大排序,并考虑如下引理——
引理:对于未选区间$x$和已选区间$y$,若$x$在$y$之前,则$x$与$y$有交
归纳证明,当区间$x$被选择时,对其分类讨论:
1.若不存在已选区间与$x$有交,结合贪心其必然为第一个未选区间(之前不存在未选区间)
2.若存在已选区间$y$与$x$有交,任取$x$之前的未选区间$z$,再对其分类讨论:
(1)若$z$在$y$之前,根据归纳假设$z$与$y$有交,结合贪心此时应选择区间$z$而不是$x$
(2)若$z$不在$y$之前,注意到$x$之前与$x$有交的区间构成后缀,进而$x$与$z$也有交
在此基础上,这$cnt_{l,r}$个区间影响范围单调不降,显然可以贪心
时间复杂度为$o(n^{2}\log n)$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 2005 4 #define fi first 5 #define se second 6 int n,P[N],lim[N],cnt[N]; 7 struct Seg{ 8 int l,r,id; 9 bool operator < (const Seg &n)const{ 10 return r<n.r; 11 } 12 }a[N]; 13 bool check(int x,int y){ 14 return (a[x].l<=a[y].r)&&(a[y].l<=a[x].r); 15 } 16 bool check(int d){ 17 for(int i=1;i<=n;i++)lim[i]=n; 18 for(int i=1;i<=n;i++){ 19 memset(cnt,0,sizeof(cnt)); 20 for(int j=1;j<=n;j++) 21 if ((lim[j])&&(lim[j]<=n))cnt[lim[j]]++; 22 for(int j=i;j<=n;j++){ 23 cnt[j]+=cnt[j-1]; 24 if (cnt[j]>j-i+1)return 0; 25 } 26 for(int j=i;j<=n;j++) 27 if (cnt[j]==j-i+1){ 28 for(int k=1;k<=n;k++) 29 if ((lim[k])&&(lim[k]<=j)){P[i]=k;break;} 30 lim[P[i]]=0; 31 for(int k=1;k<=n;k++) 32 if (check(k,P[i]))lim[k]=min(lim[k],i+d); 33 break; 34 } 35 } 36 return 1; 37 } 38 int main(){ 39 scanf("%d",&n); 40 for(int i=1;i<=n;i++){ 41 scanf("%d%d",&a[i].l,&a[i].r); 42 a[i].id=i; 43 } 44 sort(a+1,a+n+1); 45 int l=0,r=n; 46 while (l<r){ 47 int mid=(l+r>>1); 48 if (check(mid))r=mid; 49 else l=mid+1; 50 } 51 check(l); 52 for(int i=1;i<=n;i++)printf("%d ",a[P[i]].id); 53 return 0; 54 }