读书人

标题:[NOIP1999]拦截导弹(最长非递增子

发布时间: 2013-09-28 10:01:20 作者: rapoo

题目:[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,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\n",len,cnt);    }    return 0;}


读书人网 >编程

热点推荐