• 题目:[NOIP1999]拦截导弹(最长非递增子序列DP) O(n^2)和O(n*log(n))的两种做法


    题目:[NOIP1999]拦截导弹

    问题编号:217

    题目描述

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

    输入格式

    输入数据为两行,
    第一行为导弹的数目N(n<=1000)
    第二行导弹依次飞来的高度,所有高度值均为不大于30000的正整数。

    输出格式

    输出只有一行是这套系统最多能拦截的导弹数和要拦截所有导弹最少要配备这种导弹拦截系统的套数。两个数据之间用一个空格隔开.

    样例输入

    样例输出

    三维状态图像

    题目地址

    题意:

    见上。

    思路:

    首先说容易想到的o(n^2)的算法。dp[i]表示以第i个导弹结尾的最长非递增子序列。

    那么dp[i]=max(dp[j]+1).bom[j]>=bom[i]。j<i。bom[i]表示第i个导弹的高度。

    然后遍历dp[]找最大值即可。

    对于拦截的所有导弹的最少系统数。贪心思想。能拦截就用最小射程的系统拦截。不能拦就新开一个系统。

    考虑到导弹的射程满足单调性。可以二分查找。详细见代码:

     

    #include <iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    int dp[1010],hei[1010],bom[1010],cnt;
    void addf(int h)
    {
        int l,r,mid,ans=-1;
        l=0,r=cnt-1;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(hei[mid]>=h)
                ans=mid,r=mid-1;
            else
                l=mid+1;
        }
        if(ans==-1)
            hei[cnt++]=h;
        else
            hei[ans]=h;
    }
    int main()
    {
        int i,j,n,ans;
    
        while(~scanf("%d",&n))
        {
            cnt=0;
            for(i=1;i<=n;i++)
            {
                scanf("%d",bom+i);
                addf(bom[i]);
            }
            dp[1]=ans=1;
            for(i=2;i<=n;i++)
            {
                dp[i]=1;
                for(j=1;j<i;j++)
                    if(bom[j]>=bom[i])
                    dp[i]=max(dp[i],dp[j]+1);
            }
            for(i=2;i<=n;i++)
                ans=max(ans,dp[i]);
            printf("%d %d
    ",ans,cnt);
        }
        return 0;
    }
    


    对于o(n*log(n))的算法。最少系统数仍然二分。对于一个系统可以拦截最多导弹数要换一种思路。

    我们用dp[i]表示拦截导弹数为i系统的最大射程。对于bom[i]我们找到最大的j使得dp[j]>=bom[i]。那么

    dp[j+1]=max(dp[j+1],bom[i])。感觉有点贪心的思想。同样的长度使结尾最大以给后面留更多的选择余地。

    详细见代码:

     

    #include <iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    int dp[1010],hei[1010],bom[1010],cnt,len;
    void addf(int h)
    {
        int l,r,mid,ans=-1;
        l=0,r=cnt-1;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(hei[mid]>=h)
                ans=mid,r=mid-1;
            else
                l=mid+1;
        }
        if(ans==-1)
            hei[cnt++]=h;
        else
            hei[ans]=h;
    }
    void binf(int x)
    {
        int l,r,mid,ans=-1;
        l=1,r=len;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(dp[mid]>=x)
            {
                ans=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        if(ans>0)
        {
            dp[ans+1]=max(dp[ans+1],x);
            if(ans+1>len)
                len++;
        }
        else if(r<l)
            dp[1]=x;
    }
    int main()
    {
        int i,n;
    
        while(~scanf("%d",&n))
        {
            cnt=0;
            for(i=1;i<=n;i++)
            {
                scanf("%d",bom+i);
                addf(bom[i]);
            }
            dp[1]=bom[1];
            len=1;
            for(i=2;i<=n;i++)
                binf(bom[i]);
            printf("%d %d
    ",len,cnt);
        }
        return 0;
    }


  • 相关阅读:
    数据结构实验之排序八:快速排序-sdut
    青蛙过河-sdut
    汉诺塔系列2-sdut
    汉诺塔-sdut
    Fighting_小银考呀考不过四级-sdut
    【JS】只能输入数字和两位小数的JS
    jQuery form 表达验证
    JS正则表达式验证数字非常全
    在lua中优雅的操作日期和时间
    lua 判断为空的逻辑
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3343561.html
Copyright © 2020-2023  润新知