【最长不上升子序列覆盖】 拦截导弹
传送门
题意
给\(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);
}