DP - LIS - 拦截导弹 - NOIP1999

DP - LIS - 拦截导弹 - NOIP1999

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。

但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。

某天,雷达捕捉到敌国的导弹来袭。

由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

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

输入格式
共一行,输入导弹依次飞来的高度。

输出格式
第一行包含一个整数,表示最多能拦截的导弹数。

第二行包含一个整数,表示要拦截所有导弹最少要配备的系统数。

输入样例:
389 207 155 300 299 170 158 65
输出样例:
6
2


分析:

给 定 一 个 序 列 , 要 求 两 个 问 题 : ① 、 最 长 非 递 增 子 序 列 的 长 度 。 ② 、 原 序 列 最 少 能 够 分 成 几 个 最 长 非 递 增 子 序 列 。 给定一个序列,要求两个问题:\\①、最长非递增子序列的长度。\\②、原序列最少能够分成几个最长非递增子序列。

要 覆 盖 掉 整 个 原 序 列 , 那 么 每 个 数 都 要 被 覆 盖 掉 因 此 , 可 以 从 贪 心 的 角 度 思 考 , 每 次 去 除 的 非 递 增 子 序 列 一 定 要 尽 量 的 长 , 去 除 的 次 数 就 是 最 终 答 案 。 即 对 每 个 元 素 而 言 , 要 让 该 元 素 后 面 能 够 接 的 元 素 尽 量 的 多 。 要覆盖掉整个原序列,那么每个数都要被覆盖掉\\因此,可以从贪心的角度思考,每次去除的非递增子序列一定要尽量的长,去除的次数就是最终答案。\\即对每个元素而言,要让该元素后面能够接的元素尽量的多。

这 个 思 想 与 单 调 队 列 优 化 最 长 上 升 子 序 列 是 一 致 的 。 这个思想与单调队列优化最长上升子序列是一致的。

有 结 论 : 最 长 上 升 子 序 列 的 方 案 数 = 序 列 能 够 分 成 的 最 长 非 递 增 子 序 列 的 个 数 。 有结论:最长上升子序列的方案数=序列能够分成的最长非递增子序列的个数。 =

具体落实:

求 方 案 数 : 对 每 个 元 素 a i , 数 组 g [ k ] : 前 i − 1 个 元 素 已 经 构 成 的 第 k 个 非 递 增 子 序 列 的 末 尾 元 素 。 对 于 a i , 我 们 遍 历 数 组 g , 找 到 第 一 个 末 尾 元 素 大 于 a i 的 元 素 g [ k ] , 将 a i 接 在 其 后 , 那 么 此 时 g [ k ] 更 新 为 a i , 也 就 是 说 第 k 个 非 递 增 子 序 列 的 末 尾 元 素 变 成 了 a i 。 求方案数:\\对每个元素a_i,数组g[k]:前i-1个元素已经构成的第k个非递增子序列的末尾元素。\\对于a_i,我们遍历数组g,找到第一个末尾元素大于a_i的元素g[k],将a_i接在其后,那么此时g[k]更新为a_i,\\也就是说第k个非递增子序列的末尾元素变成了a_i。 aig[k]:i1kaigaig[k]aig[k]ai,kai

最 后 非 递 增 子 序 列 的 个 数 就 是 最 终 的 方 案 数 。 最后非递增子序列的个数就是最终的方案数。

拿 样 例 举 例 : 拿样例举例:
i = 1 : g [ 0 ] = 389 i=1:g[0]=389 i=1g[0]=389
i = 2 : g [ 0 ] = 207 i=2:g[0]=207 i=2g[0]=207
i = 3 : g [ 0 ] = 155 i=3:g[0]=155 i=3g[0]=155
i = 4 : g [ 0 ] = 155 , g [ 1 ] = 300 i=4:g[0]=155,g[1]=300 i=4g[0]=155g[1]=300
i = 5 : g [ 0 ] = 155 , g [ 1 ] = 299 i=5:g[0]=155,g[1]=299 i=5g[0]=155g[1]=299
i = 6 : g [ 0 ] = 155 , g [ 1 ] = 170 i=6:g[0]=155,g[1]=170 i=6g[0]=155g[1]=170
i = 7 : g [ 0 ] = 155 , g [ 1 ] = 158 i=7:g[0]=155,g[1]=158 i=7g[0]=155g[1]=158
i = 8 : g [ 0 ] = 65 , g [ 1 ] = 158 i=8:g[0]=65,g[1]=158 i=8g[0]=65g[1]=158

代码:

#include<iostream>
#include<algorithm>

using namespace std;

const int N=1010;

int n,a[N],f[N],g[N];

int main()
{
    while(cin>>a[++n]);
    
    int res=0;
    for(int i=1;i<n;i++)
    {
        f[i]=1;
        for(int j=1;j<i;j++)
            if(a[i]<=a[j])
                f[i]=max(f[i],f[j]+1);
        res=max(res,f[i]);
    }
    
    cout<<res<<endl;
    
    int cnt=0;
    for(int i=1;i<n;i++)
    {
        int k=0;
        while(k<cnt&&g[k]<a[i]) k++;
        g[k]=a[i];
        if(k>=cnt) cnt++;
    }
    
    cout<<cnt<<endl;
    
    return 0;
    
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值