拦截导弹——动态规划
拦截导弹
https://ac.nowcoder.com/acm/problem/16810
题目描述
链接:https://ac.nowcoder.com/acm/problem/16810
来源:牛客网
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
题目思路
一套系统最多能拦截多少导弹?
动态规划,本质是找所给数组a的最长不递增子序列。建立数组dp保存被拦截的导弹序列。遍历数组a,对于每一个元素a[j],如果有a[j]<=dpi,将a[j]加入dp数组;反之,找到dp数组中恰小于a[j]的元素位置k,使得dp[k]=a[j]。
至少需要多少导弹拦截系统?
引入Dilworth定理:对偏序集<A,≤>,设A中最长链的长度是n,则将A中元素分成不相交的反链,反链个数至少是n。
因此只需要找到数组a中的最长递增子序列即可。
如下图所示。
完整代码
(代码中的bin和bin2函数可以分别用upper_bounder和lower_bounder函数代替,使用时注意形参数组需要有序,使用细节可参考博客:https://blog.csdn.net/qq_40160605/article/details/80150252)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100010;
int dp[MAXN], a[MAXN], res[MAXN];
int bin (int dp[MAXN], int x, int l, int r)
{
int mid = (l+r)/2;
while(l<=r)
{
mid = (l+r)/2;
if(x>dp[mid])
{
r = mid-1;
}
else if(x<dp[mid])
{
l = mid+1;
}
else
{
return mid+1;
}
}
return l;
}
int bin2 (int dp[MAXN], int x, int l, int r)
{
int mid = (l+r)/2;
while(l<=r)
{
mid = (l+r)/2;
if(x<dp[mid])
{
r = mid-1;
}
else if(x>dp[mid])
{
l = mid+1;
}
else
{
return mid;
}
}
return l;
}
int main()
{
int i=0;
while(scanf("%d",&a[i])!=EOF)
{
i++;
}
int len = i;
i=0;
dp[0] = a[0];
for(int j=1;j<len;j++)
{
if(a[j]<=dp[i])
{
i++;
dp[i] = a[j];
}
else
{
// int ind = upper_bound(dp,dp+i,a[j],greater<int>())-dp;
int ind = bin(dp, a[j], 0, i);
dp[ind] = a[j]+1;
}
}
i++;
printf("%d\n", i);
res[0] = a[0];
i = 0;
for(int j=1;j<len;j++)
{
if(a[j]>res[i])
{
i++;
res[i] = a[j];
}
else if(a[j]<res[i])
{
//int ind = lower_bound(res,res+i,a[j])-res;
int ind = bin2(res, a[j], 0,i);
res[ind] = a[j];
}
}
printf("%d", ++i);
return 0;
} 
