题意
就是给出一个序列,然后找出最少数量的非递增序列,按照题目的意思,每一个非递增的序列就是一个拦截系统。
题解
来自洛谷的一位大佬
假设现在有k套拦截系统,目前每套系统的拦截高度为dp[i],(1<=i<=k)
,那么现在要用这几套系统拦截另外一个高度为h
的导弹p,则必须满足条件
m
a
x
1
<
=
i
<
=
k
(
d
p
[
i
]
)
>
h
max_{1<=i<=k}(dp[i]) > h
max1<=i<=k(dp[i])>h,换句话说就是这几个方案中必须存在方案能使得高度为h
的导弹p被拦截,就是说dp[i]
中必须有一个值比h
大,如果可以拦截呢,那么必须要让那个dp值最接近h且比h大的拦截系统进行拦截(这句话是没有证明的,我们再后面进行说明),于是按照这样的思路有如下代码:
ps:
那么必须要让那个dp值最接近h且比h大的拦截系统进行拦截
这句话实际上没有证明这样的算法是最优的,我们可以很轻松的证明出来如果一定要用以往的方案进行拦截的话(我没有办法证明不用以往的方案,也就是新开辟一个方案一定不是最优的!如果有人会的话可以和我交流以下!!),那么使用dp值比h大且最接仅的方案一定是最优秀的,如下图所所示。
我们把每个方案拿出来,dpi
就是方案i可以接受的最大导弹高度,假设现在有一个高度为h
的导弹,那么方案3,4,5
可以将导弹拦截,我们可以发现,使用不同方案拦截后损失的可拦截高度时不一样的,比如用方案3
拦截,损失高度为[h,dp3]
,也就是说,这个区间的导弹会少拦截一颗(不是不能拦截,因为其他方案还可以拦截这个高度的导弹,值不过会少拦截一颗),使用方案4
拦截,损失高度为[h,dp4]
,这个区间会少拦截一颗导弹,相比于在[h,dp3]
区间少拦截一颗导弹,使用方案4
还会多出一段[h3,h4]
区间少拦截一颗导弹,这是不合适的,使用方案5
同理,所以要选择且最大拦截值dp离h最近且比h大的方案。
// 题解https://www.luogu.com.cn/problem/solution/P1020
// #include<bits/stdc++.h>
#include<iostream>
using namespace std;
const int maxx = 2000 + 5;
int shell[maxx];
int dp[maxx]; // 表示第k套拦截系统的最小值
int find(int k,int val) // 假设我们现在有k套系统。
{
int l = 0, r = k - 1;
while(l<r){ // l == r break; l和r中恰好有val的上限
int mid = (l + r)>>1;
if(dp[mid] < val){
l = mid + 1;
}
else {
r = mid;
}
} // dp[r] >= val
if(dp[r] < val) r = r + 1; // 添加下一个系统
return r;
}
int main()
{
int n;
int ans;
while(scanf("%d",&n) == 1){
for(int i=0;i<n;i++)scanf("%d",&shell[i]);
if(n == 0){
cout<<0<<endl;
continue;
}
ans = 1;
dp[0] = shell[0];
for(int i = 1; i < n; i++){
int posi = find(ans, shell[i]);
if(posi == ans)ans++;
dp[posi] = shell[i];
}
cout<<ans<<endl;
}
return 0;
}