BTTCOJ 1072: 拦截导弹 最长上升子序列

题目描述

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

输入格式

每个测试文件只包含一组测试数据,每组输入若干个整数,表示导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)。

输出格式

对于每组输入数据,第一行输出这套系统最多能拦截多少导弹,第二行输出如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入样例

389 207 155 300 299 170 158 65

输出样例

6
2

解题思路

第一次系统的学习了线性dp,做了这个递推型动规题解,解题思路分三步:

第一步先找子问题,求n个数的最长子序列的长度的子问题就是求前n个最长子序列的长度。

第二步确定状态,子问题只和一个变量有关,那就是数字的位置,那么就很好得出数字的位置k就是数字的状态,而状态k对应的值,就是以a[k]做为重点的最长上升子序列的长度。

第三步找出状态转移方程,首先初始状态lis[0]=1,因为只有一个数字,而当k>=1时,有方程max_len(k)=(max_len(i):1<=i<k&&a[i]<a[k])+1,若找不到这样的i,则max_len(k)=1。max_len的值就是在a[k]左边,终点数值小于a[k],且长度最大的上升子序列的长度+1,因为a[k]左边任何终点小于a[k]的子序列,加上a[k]后就能形成一个更长的上升子序列。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=100005;
int a[maxn];
int lis[maxn];
int dp[maxn];
int main()
{
    int n;
    int k=0;
    while(cin>>n)//ctrl+z结束输入
    {
        a[k]=n;//存输入的数据
        lis[k]=1;//记录下降子序列
        dp[k++]=1;//记录上升子序列
    }
    for(int i=1;i<k;i++)
    {
        for(int j=0;j<i;j++)
        {
            if(a[j]>=a[i])//寻找最长下降子序列,最多能拦截的炮弹数
            {
                lis[i]=max(lis[i],lis[j]+1);
            }
        }
    }
    cout<<*max_element(lis,lis+k)<<endl;//取lis数组中的最大值,注意带*取值
    for(int i=1;i<k;i++)
    {
        for(int j=0;j<i;j++)
        {
            if(a[j]<a[i])//寻找最长上升子序列,也就是需要多少台设备
            {
                dp[i]=max(dp[i],dp[j]+1);
            }
        }
    }
    cout<<*max_element(dp,dp+k)<<endl;//取dp数组中的最大值,注意带*取值
    return 0;
}
发布了6 篇原创文章 · 获赞 18 · 访问量 200

猜你喜欢

转载自blog.csdn.net/qq_44758960/article/details/104910566