[洛谷] [P1020] 导弹拦截
[洛谷] [P1020] 导弹拦截
题目链接:[P1020]([P1020 NOIP1999 普及组] 导弹拦截 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是\(\le 50000\)的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
1行,若干个整数(个数 \(\le 100000\))
输出格式
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
题解
第一问:一套拦截系统最多拦截多少导弹
我们可以抽出高度序列的子序列,使得这个子序列是单调不增加的,显然这是单调子序列模型,求出最长单调不增加的子序列长度即可
第二问:拦截所有导弹最少需要多少套系统
由于拦截系统的数量要最少,因此每个系统都要尽其所能。
假设第一套系统拦截了某些导弹,这些导弹的高度序列是当前序列的最长单调不增加子序列。
上一个拦截系统要某些导弹拦截后,剩下的导弹由第二套系统拦截。
因此,需要的最少的拦截系统的数量就是原序列可以拆分为的单调不增加序列的最少序列数,可以由 \(Dilworth\) 定理得到:
单调不增加序列的最少拆分数等于该序列的最长单调增加序列的长度
题解代码
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;//100010;
int h[N],f[N];
int main(){
int n = 0;
while(cin>>h[n])n++;
//第一遍解单调子序列
//f[i]表示长度为 i-1 的单调不上升子序列的最大末尾元素
int maxL = 1;
f[0] = h[0];
for(int i = 1;i<n;i++){
if(f[maxL-1] >= h[i])f[maxL++] = h[i];
else{
//找到第一个小于h[i]的f[i]
int l = 0, r= maxL - 1;
//[l,mid][mid+1,r]
while(l<r){
int mid = (l+r)/2;
if(f[mid] < h[i])r = mid;
else l = mid + 1;
}
f[l] = h[i];
}
}
cout<<maxL<<endl;
//第二遍求解单调子序列
//f[i]代表长度为 i-1 的单调上升子序列的最小末尾元素
maxL = 1;
memset(f,0,sizeof f);
f[0] = h[0];
for(int i = 1;i<n; i++){
if(f[maxL - 1]<h[i])f[maxL++] = h[i];
else{
//找到第一个大于等于h[i]的f[i]
*lower_bound(f,f+maxL,h[i]) = h[i];
}
}
cout<<maxL<<endl;
return 0;
}
本文来自博客园,作者:Sarfish,转载请注明原文链接:https://www.cnblogs.com/sarfish/p/15935554.html

浙公网安备 33010602011771号