拦截导弹

问题 J: 拦截导弹

题目描述

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

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

输入

1行,若干个整数(≤100000),为导弹依次飞来的高度

输出

2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

样例输入

389 207 155 300 299 170 158 65

样例输出

6
2

分析

题目有两点要求:

  1. 算出最长下降子序列
  2. 算出下降子序列的个数

对于第一点,把数组倒过来,用lower_bound()算出最长递增子系列,就可以了,对于第二点,有一个什么定理,相当于计算原来数列最长上升子序列的长度,计算方法一样

代码

int v[1000005];
int dp[1000005],f[1000005];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    //
    // freopen("E:/Code/C++/untitled1/input.txt","r",stdin);
    // freopen("output.txt","w",stdout);


    mem(v);mem(dp);mem(f);//初始化
    int t,cnt = 0;
    while (cin >> t)
    {
        v[cnt++] = t;
    }//cnt = size
//算第二点
    f[1] = v[0];//边界,记录长度为1的子序列的最小末尾值
    dp[0] = 1;//记录长度
    int cnt_f = 1;//记录最大的子序列长度
    for(int i = 0;i < cnt;++i)
    {
        auto h = lower_bound(f+1,f+cnt_f + 1,v[i])-(f+1);//使用二分查找找到v[i]比哪个长度的子序列末端大
        f[h+1] = v[i];//更新子序列末端的值
        if(h+1>cnt_f) cnt_f = h+1;//更新最大子序列长度
        dp[i] = h+1;//de3(t,v[i],dp[i]);
    }
    int ans = cnt_f;
//算第一点
    mem(f);mem(dp);
    for(int i = 0;i < cnt/2;++i)
        swap(v[i],v[cnt-i-1]);

    cnt_f = 1;
    dp[0] = 1;
    f[1] = v[0];
    for(int i = 1;i < cnt;++i)
    {
        auto h = lower_bound(f+1,f+cnt_f + 1,v[i]) - (f + 1);
        f[h+1] = v[i];
        if(h+1 > cnt_f) cnt_f = h+1;
        dp[i] = h + 1;
    }

    cout << cnt_f << endl << ans;
}
posted @   bakul  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示