noip1999(提高组) 导弹拦截

来源:互联网 发布:乡镇网络舆情自查报告 编辑:程序博客网 时间:2024/06/06 00:30
题目描述

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

输入格式

输入数据为两行,

第一行为导弹的数目N(n<=1000)

第二行导弹依次飞来的高度,所有高度值均为不大于30000的正整数。

输出格式

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

样例输入

8
389 207 155 300 299 170 158 65

样例输出

6 2


解析

  第一个问题很好理解,显然是求最大非升子序列。经典的动规模型,f[i]表示前i个导弹的最长非升子序列,那么f[i]就是在保证h[j]>=h[i](非升)的前提下前i-1个f[j]中最大的f[j]+1。

<span style="font-size:14px;">//最长非升子序列for(int i=1;i<=n;i++) f[i]=1;for(int i=1;i<=n;i++)for(int j=1;j<i;j++)if( (h[j]>=h[i])&&(f[j]+1>f[i]) )f[i]=f[j]+1;</span>
  第二个问题很容易想到每次求最长非升子序列,然后把这些元素删除。再求一次最长非升子序列,然后把这些元素删除。如此重复,直到全部删除。但是这里局部最优不是整体最优。

  举个反例:6 1 7 3 2  错解:6 3 2/1/7  正解:6 1/7 3 2

  换个角度考虑 1.所有导弹都要打下来 2.必须是不严格的单调递减  结论:只要把最长上升子序列都打下来了,所有导弹就可以打下来。那么问题就抽象成了求最长上升子序列

<span style="font-size:14px;">//最长上升子序列for(int i=1;i<=n;i++) f[i]=1;for(int i=1;i<=n;i++)for(int j=1;j<i;j++)if( (h[j]<h[i])&&(f[j]+1>f[i]) )f[i]=f[j]+1;</span>
下面给出完整的代码:
<span style="font-size:14px;">#include<cstdio>#include<cstring>using namespace std;int f[1100],h[1100],n;void readdata(){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&h[i]);}}void write(){int max=0;for(int i=1;i<=n;i++)max=(max<f[i])?f[i]:max;printf("%d ",max);}void work(){//最长非升子序列for(int i=1;i<=n;i++) f[i]=1;for(int i=1;i<=n;i++)for(int j=1;j<i;j++)if( (h[j]>=h[i])&&(f[j]+1>f[i]) )f[i]=f[j]+1;write();//最长上升子序列for(int i=1;i<=n;i++) f[i]=1;for(int i=1;i<=n;i++)for(int j=1;j<i;j++)if( (h[j]<h[i])&&(f[j]+1>f[i]) )f[i]=f[j]+1;write();}int main(){freopen("127.in","r",stdin);freopen("127.out","w",stdout);readdata();work();return 0;}</span>


0 0
原创粉丝点击