• 导弹拦截(最长子序列问题,二分查找)


    题目描述

    某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

    输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

    输入输出格式

    输入格式:

    111行,若干个整数(个数≤10000)

    输出格式:

    2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

    思路:

    题意已经明显告诉你让你求一个最长不上升子序列和一个最长上升子序列

    O(n^2)的算法大家应该都会,现在的问题是如何优化

    我们以第一问为例

    首先记下在当前位置之前长度为k的最长不下降子序列的结尾最大是什么(如果没有,就记为0)

    然后,我们得出一个单调性:长度越长,必然高度越低

    (因为一个较长的必定由一个较短的转移而来,由最长不上升子序列的性质可得,后者高度小于等于前者高度)

    我们就可以用二分查找的方式找到转移点转移

    不过记住,转移后要更新长度为当前位置长度的最长不下降子序列的结尾

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define rii register int i
    #define rij register int j
    using namespace std;
    int dp[100005],n,m,cd[100005],h[100005],ans;
    int main()
    {
        for(rii=0;i<=100005;i++)
        {
            cd[i]=-1;
        }
        cd[0]=2147483647;
        n=1;
        while(~scanf("%d",&h[n]))
        {
            n++;
        }
        n--;
        for(rii=1;i<=n;i++)
        {
            int l=0,r=i;
            while(l<r)
            {
                if(r-l==1)
                {
                    if(cd[r]>=h[i])
                    {
                        dp[i]=r+1;
                        cd[r+1]=max(cd[r+1],h[i]);
                    }
                    else
                    {
                        dp[i]=l+1;
                        cd[l+1]=max(cd[l+1],h[i]);
                    }
                    break;
                }
                int mid=(l+r)/2;
                if(cd[mid]<h[i])
                {
                    r=mid;
                }
                else
                {
                    l=mid;
                }
            }
            ans=max(ans,dp[i]);
        }
        cout<<ans<<endl;
        for(rii=0;i<=100005;i++)
        {
            cd[i]=2147483647;
        }
        ans=0;
        memset(dp,0,sizeof(dp));
        cd[0]=0;
        for(rii=1;i<=n;i++)
        {
            int l=0,r=i;
            while(l<r)
            {
                if(r-l==1)
                {
                    if(cd[r]<h[i])
                    {
                        dp[i]=r+1;
                        cd[r+1]=min(cd[r+1],h[i]);
                    }
                    else
                    {
                        dp[i]=l+1;
                        cd[l+1]=min(cd[l+1],h[i]);
                    }
                    break;
                }
                int mid=(l+r)/2;
                if(cd[mid]>=h[i])
                {
                    r=mid;
                }
                else
                {
                    l=mid;
                }
            }
            ans=max(ans,dp[i]);
        }
        cout<<ans;
    }
  • 相关阅读:
    exFAT移动硬盘写保护怎么去掉
    ORACLE:一列的多行数据拼成字符串
    cxgrid中,如何根据列名或字段名取得footer值
    Delphi天气预报查询
    Delphi ListView基本用法大全
    datasnap的初步
    Delphi ListView基本用法大全
    Delphi实现树型结构
    获取身份证号码信息
    让Delphi XE5跟其他版本的Delphi共存
  • 原文地址:https://www.cnblogs.com/ztz11/p/9807236.html
Copyright © 2020-2023  润新知