洛谷 P1020 导弹拦截 最长不上升子序列(n²/nlogn)

P1020 导弹拦截

  • 时空限制1s / 128MB

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是不大于50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入输出格式

输入格式:

 

一行,若干个整数(个数少于100000)

 

输出格式:

 

2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

 

输入输出样例

输入样例#1:
389 207 155 300 299 170 158 65
输出样例#1:
6
2
----------------------------------------------------------------------

这道题第一问可以用动规做,算法复杂度N方;第二问直接贪心
都很容易理解,详细看代码:

 

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define maxn 100010
 5 using namespace std;
 6 int a[maxn],l[maxn],n,ans,total;
 7 bool flag,f[maxn];
 8 int main(){
 9     n=1;ans=0;total=0;
10     while(scanf("%d",&a[n])!=EOF) n++;
11     n--;
12     for(int i=n;i>0;i--)//l[i]表示第i个数到第n个数这段区间中的最长不上升子序列的长(注意,这里的第i个数一定是l[i]存的最长序列的队头) 
13        for(int j=i;j<=n;j++)
14        if(a[i]>=a[j]&&l[j]+1>l[i]) l[i]=l[j]+1; 
15     for(int i=1;i<=n&&n-i+1>ans;i++)//因为 l[i]里存的最长序列的队头一定是i,所以需要这个枚举操作,来找出总序列的最长不上升子序列 
16     ans=max(ans,l[i]);
17     flag=true;
18     memset(f,0,sizeof(f));
19     while(1){
20         int top=0,topl;
21         flag=false;
22         for(int i=1;i<=n;i++)
23         if(!f[i]){
24             top=a[i];
25             topl=i;
26             flag=true;
27             break;
28         }
29         if(!flag) break;
30         for(int i=topl;i<=n;i++)
31         if(!f[i]&&top>=a[i]){
32             f[i]=true;
33             top=a[i];
34         }
35         total++;
36     }
37     printf("%d\n%d",ans,total);
38     return 0;
39 }
n方

 

但以上N方的方法在洛谷上会T,于是请教了Brian551神犇,把复杂度降到nlogn

首先我们要知道一个定理

最小链覆盖数=最长反链大小

放到这道题中,因为题目求的是最长不上升子序列的长度,所以 最小严格升序列覆盖数=最长不上升子序列长度

所以我们就只需要求出这条序列中的最小严格升序列覆盖数,就能得出第一问的答案了

详细的以后补充,最近没时间

 

AC代码:

 

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #define maxn 100010
 5 using namespace std;
 6 int n,a[maxn],cnt,w[maxn],total;
 7 bool flag,f[maxn];
 8 int binary(int);
 9 int main(){
10     memset(f,0,sizeof(f));
11     n=1;cnt=0;total=0;
12     while(scanf("%d",&a[n])!=EOF) n++;
13     n--;
14     w[++cnt]=a[1]; 
15     for(int i=2;i<=n;i++){
16         int s=binary(a[i]);
17         if(s>cnt) w[++cnt]=a[i];
18         else w[s]=a[i];
19     }
20     while(1){//第二问依旧是贪心做法 
21         int top=0,topl;
22         flag=false;
23         for(int i=1;i<=n;i++)
24         if(!f[i]){
25             top=a[i];
26             topl=i;
27             flag=true;
28             break;
29         }
30         if(!flag) break;
31         for(int i=topl;i<=n;i++)
32         if(!f[i]&&top>=a[i]){
33             f[i]=true;
34             top=a[i];
35         }
36         total++;
37     }
38     printf("%d\n%d",cnt,total);
39     return 0;
40 }
41 int binary(int k){
42     int l=1,r=cnt;
43     while(l<=r){
44         int mid=(l+r)>>1;
45         if(w[mid]>=k) l=mid+1;//因为是严格上升,所以需要>= 
46         else r=mid-1; 
47     }
48     return l;
49 }
nlogn

 

posted @ 2017-10-20 22:11  lpl_bys  阅读(565)  评论(0编辑  收藏  举报