P1020 导弹拦截

P1020 导弹拦截

   知识铺垫:

  upper_bound(f+1,f+n+1,key)-f返回在不降序列f中大于key的第一个元素的下标

  lower_bound(f+1,f+n+1,key)-f返回在不降序列f中大于等于key的第一个元素的下标

  如果要在非升序列(小于和小于等于)中使用这两个函数,就需要重载运算符

    bool cmp(const int& a,const int& b){return a > b;} 

  • 最长上升子序列问题(Longest Increasing Subsequence,LIS):给定一个长度为n的序列a,求数值单调递增的子序列的长度最长是多少。a的任意子序列b可表示为b={a[k1],a[k2],...,a[kp]},其中k1<k2<...<kp。
    • O(n^2)求法:f[i]表示以a[i]为结尾的LIS的长度。则有f[i]=max{f[j]+1},0<j<i,a[j]<a[i],边界:f[0]=0。目标:max(f[i]},1<=i<=n。
    • O(nlogn)求法:f[i]表示长度为i的LIS的结尾元素,初始化ans=0,从1到n遍历a,每遇到a[i]>f[ans],就使f[++ans]=a[i]。否则:根据f[]的定义可知,f[]数组是单调递增的,所以可以用二分查找找到p使得f[p]>=a[i],f[p-1]<a[i]令f[p]=a[i](用lower_bound()即可),即找到第一个大于等于a[i]的f[p](lower_bound),令f[p]=a[i](因为a[i]的潜力更大),平均复杂度logn,所以总时间复杂度为nlogn
  • 最长下降子序列:查找时找第一个小于等于a[i]的元素(因为此时f[]序列是非升的,所以需要重载运算符,用lower_bound)
  • 最长不下降:a[i]>=f[ans] 则f[++ans]=a[i],否则找到第一个大于a[i]的f[p](upper_bound),
  • 最长不上升:a[i]<=f[ans] 则f[++ans]=a[i],否则找到第一个小于a[i]的f[p](也需要重载运算符,用upper_bound),

题意:

给一个长度不超过100000的数列,其中每一个数都是不超过50000的正整数,第一问求这个序列的最长不上升子序列,第二问求将这个序列分为n个不上升子序列时n的最小值。

   解题思路:

  1. 第一问:直接求最长不上升子序列即可。

  2. 第二问:可以用贪心或者有一个Dilworth定理,可知直接求该序列的最长上升子序列的长度即可。

  代码:

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <cctype>
 6 using namespace std;
 7 
 8 #define res register int
 9 inline int read()
10 {
11     int x(0),f(1); char ch;
12     while(!isdigit(ch=getchar())) if(ch=='-') f=-1;
13     while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
14     return f*x;
15 }
16 
17 const int N=100000+10; 
18 int f1[N],f2[N],a[N],n,ans1,ans2;
19 inline bool cmp(const int&a,const int&b){return a>b;}
20 
21 int main()
22 {
23     while(~scanf("%d",&a[++n]));
24     n--;
25     f1[1]=f2[1]=a[1]; ans1=ans2=1;
26     for(res i=2 ; i<=n ; i++)
27     {
28         if(a[i]<=f1[ans1]) 
29             f1[++ans1]=a[i];
30         else 
31             *upper_bound(f1+1,f1+ans1+1,a[i],cmp)=a[i];
32         if(a[i]>f2[ans2]) 
33             f2[++ans2]=a[i];
34         else
35             *lower_bound(f2+1,f2+ans2+1,a[i])=a[i];
36     }
37     printf("%d\n%d\n",ans1,ans2);
38     return 0;
39 }
View Code

 

转载于:https://www.cnblogs.com/wmq12138/p/10363793.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值