题目描述
链接: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; }