题目描述

链接: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;
}