【最长不上升子序列覆盖】 拦截导弹

传送门

题意

\(n\)个导弹的高度,拦截导弹,导弹拦截系统的第一发能到达任意高度,之后的导弹不能高于第一发的高度,
首先求出一组拦截设施能够最多拦截的导弹个数,
其次求出最少使用几组拦截导弹能够拦截所有导弹

数据范围

\(1\leq n\leq 100000\)

题解

最多拦截的导弹数量就是最长不上升子序列长度

  • 状态表示:\(dp[i]\)表示长度为\(i\)的子序列的结尾
  • 对于每个新的元素,贪心的二分找到第一个小于其值的进行替换
  • 由于\(dp\)内部是有序的并且找到的是第一个小于其值的,所以之前的一定都是大于其值
  • \(upper\_bound(a.st\; ,\; a.ed\; ,\; x \;,\; greater<int>())-a.st\) 返回的就是数组中第一个\(<x\) 的下标
    拦截所有需要的导弹组数就是最长上升子序列,每个上升子序列中的元素即覆盖的每个最长不上升子序列的端点
  • 同理通过\(lower\_bound\)找到第一个\(\geq x\)的进行替换即可
  • \(dp\)内部是递增的,所以上一个一定小于找到的第一个$\geq $x 的

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)

const int N=1e5+10;

int a[N],dp[N],dpp[N];
int n=1;
int main(){
    while(scanf("%d",&a[n])!=EOF) n++;
    n--;
    
    dp[1]=a[1];
    int len=1;
    rep(i,2,n){
        if(a[i]<=dp[len]) dp[++len]=a[i];
        else dp[upper_bound(dp+1, dp+1+len, a[i], greater<int>()) - dp]=a[i];
    }
    printf("%d\n",len);
    
    len=1;
    dpp[1]=a[1];
    
    rep(i,2,n){
        if(a[i]>dpp[len]) dpp[++len]=a[i];
        else dpp[lower_bound(dpp+1,dpp+1+len,a[i])-dpp]=a[i];
    }
    printf("%d\n",len);
}
posted @ 2020-11-09 16:22  Hyx'  阅读(101)  评论(0编辑  收藏  举报