导弹拦截 nlogn 单调最长子序列

大家好我来填坑了QwQ     

题目戳这里

在刚刚接触DP和贪心的时候,教练给我们讲的例题就是导弹拦截,那个时候的标准复杂度是O(n^2)的,后来不知道什么时候洛谷把这个题的数据加强了,给了200分的O(nlogn)复杂度

下面是正文:

首先是对中心思想的阐述:一个序列中的单调子序列的个数等于最长单调序列的长度(叫做Dilworth定理,我也没推出来,但这不妨碍我们用它)

第一个问题

在第一个问题中,lis[len] 存储长度为len的不下降子序列结束点的最大值(也由此可以看出,我们无法得到这个子序列,只能得到长度)

这个最大值运用了贪心的思想,显然我们记录结束点的最大值是对接下来的情况的最好状态。

初始化将len=1,lis[len]=a[1],sil[len]=-a[1]

遍历一边原序列(2~n),依次判断a[i]与lis[i]的大小关系,我们要求的是最长不上升子序列所以len[len]与a[i]的关系为:

1         if(a[i]<=lis[len]) len++,lis[len]=a[i],sil[len]=-a[i];
2         else{
3             int t;
4             t=upper_bound(sil+1,sil+len+1,-a[i])-sil;
5             lis[t]=a[i];
6             sil[t]=-a[i];
7         }
点一下加号

a[i]小于等于lis[len],说明a[i]比目前最长的不升序列的最大结束点要小,则该序列的长度就可以增加了,目前新的lis[len]存储的便是a[i]

a[i]大于lis[len],说明a[i]并不能是len增加,但是a[i]可以更新某一个长度为t的不升序列的结束点最大值

我们使用upper_bound()来查找这个t ,维护一个sil[t]数组存储lis[t]的相反数,来维护不升序列upper_bound()

关于upper_bound()和lower_bound()的用法,在另一篇随笔里有提到,大家可以去看看  指路二分

第二个问题

1     len=1;
2     lis[1]=a[1];
3     for(int i=2;i<=n;i++){
4         if(a[i]>lis[len]) len++,lis[len]=a[i];
5         else{
6             int t=lower_bound(lis+1,lis+len+1,a[i])-lis;
7             lis[t]=a[i];
8         }
9     }
点一下加号

求最长不降子序列,思想和第一个问题一样,几乎没有差别,不过我们这里不需要sil的维护了

 1 #include<bits/stdc++.h> 
 2 using namespace std;
 3 const int maxn=100010;
 4 int n;
 5 int a[maxn];
 6 int lis[maxn];
 7 int sil[maxn];
 8 int len=1;
 9 int main(){
10     n=1;
11     while(cin>>a[n]) n++;
12     n--;
13     lis[1]=a[1];
14     sil[1]=-a[1];
15     for(int i=2;i<=n;i++)
16     {
17         if(a[i]<=lis[len]) len++,lis[len]=a[i],sil[len]=-a[i];
18         else
19                {
20             int t;
21             t=upper_bound(sil+1,sil+len+1,-a[i])-sil;
22             lis[t]=a[i];
23             sil[t]=-a[i];
24         }
25     }
26     cout<<len<<endl;
27     memset(lis,0,sizeof(lis));
28     memset(sil,0,sizeof(sil));
29     len=1;
30     lis[1]=a[1];
31     for(int i=2;i<=n;i++)
32        {
33         if(a[i]>lis[len]) len++,lis[len]=a[i];
34         else
35                {
36             int t=lower_bound(lis+1,lis+len+1,a[i])-lis;
37             lis[t]=a[i];
38         }
39     }
40     cout<<len<<endl;
41     return 0;
42 }
所有代码

猜你喜欢

转载自www.cnblogs.com/cheng-qing/p/9787423.html