洛谷p1020 导弹拦截
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出格式
输入格式:
1行,若干个整数(个数≤100000)
输出格式:
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
输出样例#1:
6 2
————————————————————————分割线——————————————————————————
第一问:
第一问其实看似没有思路,最多能拦截多少导弹,但其实,再看一下题目,发现这个拦截系统能拦截的导弹高度其实是呈不上升序列的
那么最多能够拦截的很明显就是最长不上升子序列,拿这一问就简单了
不过,平常学的dp做法都是n^2做法,看看数据100000,很明显超时了
所以我这里介绍一种nlogn的做法(其他同理,包括最长不下降子序列等):
定义一个f数组,其中f[i]表示长度为i不上升子序列的最后一位数
len表示最长不上升子序列长度 初始化len=0;f[0]=1e9 (正的无限大)
那么,如果A[i](1<=i<=number;表示导弹高度)<=f[len] f[++len]=high[i]
什么意思呢?若当前导弹高度小于等于长度为len的不上升子序列的最后一个数,那么代表这个数是可以加入这个子序列的,此时f[len]要更新成为这个数
否则的话,在f[0——len]中间二分,寻找第一个大于等于A[i]的数f[j],找到以后,再把A[i]加入到此时f[j]末尾
代码如下(有点乱,有压行的行为):
F[1] = A[1];
for (int i = 2; i <= Number; i++){//cout << A[i] << " ";
if (A[i] <= F[Answer1]){
Answer1 ++;
F[Answer1] = A[i];
continue;
}
int l = 1, r = Answer1;
while (l < r){
int Mid = (l + r) >> 1;
if (F[Mid] >= A[i] ) l = Mid + 1;
else r = Mid;
}
F[l] = A[i];
}
cout << Answer1 << endl;
第二问:
刚看到第二问,就想到了贪心,不过很明显超时了(毕竟是n方)
那我们可以推一下
假设一个序列为:7 2 3 9 7 2 3 8 n=8
很明显,手推出来需要4个
用柱形图表示如下:

那么序号为:1,3,6为一个,4,5,7为一个,2,8各一个,合起来为四个
若将一组中柱形图顶部连接起来大概若为下图:

那么可以发现每条直线的左端点(若为点则为这个点)呈上升趋势
证明:
用反证法
共ans条直线(ans为答案),dd[i]为第i条直线左端点数值
假设dd[i-1]>=dd[i],
那么dd[i-1]这个的应该存在于第i条直线上(直线呈不上升趋势),可以用一个拦截系统拦截
所以与题意相矛盾,故证得dd[i-1]<dd[i](2<=i<=n)
所以这个问题就转换成为求最长上升子序列
nlongn的方法在上一问已经说明了,但:注意:f[0]=-1e9(负的无限大)
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int _ = 1e5 + 1;
int Number = 1, A[_], F[_], Answer1 = 1, Answer2 = 1, Dp[_];
int read(){
int s = 0,w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') w = (ch == '-') ? -1 : 1, ch = getchar();
while (ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
return s * w;
}
signed main(){
while (scanf ("%lld", &A[Number]) != EOF)
Number ++;
Number -- ;
//cout << Number << endl;
F[1] = A[1];
for (int i = 2; i <= Number; i++){//cout << A[i] << " ";
if (A[i] <= F[Answer1]){
Answer1 ++;
F[Answer1] = A[i];
continue;
}
int l = 1, r = Answer1;
while (l < r){
int Mid = (l + r) >> 1;
if (F[Mid] >= A[i] ) l = Mid + 1;
else r = Mid;
}
F[l] = A[i];
}
cout << Answer1 << endl;
Dp[1] = A[1];
for (int i = 2; i <= Number; i++){
if (A[i] > Dp[Answer2]){
Answer2 ++;
Dp[Answer2] = A[i];
continue;
}
int l = 1, r = Answer2;
while (l < r){
int Mid = (l + r) >> 1;
if (Dp[Mid] < A[i]) l = Mid + 1;
else r = Mid;
}
Dp[l] = A[i];
}
cout << Answer2;
return 0;
}
如果本蒟蒻有没有讲清楚的地方,可以自己画图模拟,或许可以帮助你
(洛谷AC200题纪念)orz

浙公网安备 33010602011771号