zoukankan      html  css  js  c++  java
  • 洛谷 P1020导弹拦截题解

    洛谷链接:https://www.luogu.org/problem/P1020

    题目描述

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

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

    输入格式

    11行,若干个整数(个数le 100000100000)

    输出格式

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

    输入输出样例

    输入 #1
    389 207 155 300 299 170 158 65
    输出 #1
    6
    2
    

    说明/提示

    为了让大家更好地测试n方算法,本题开启spj,n方100分,nlogn200分

    每点两问,按问给分


    题解

    这道题现在基本上已经是DP的入门问题了。

    第一问实际是求一个最长不降子序列,而第二问是求最长上升子序列。

    最容易理解的O(n2)的解法,而O(nlogn)的解法就费解一些了。关于这部分的原理写得最通俗易懂的是的文章:https://blog.csdn.net/a342374071/article/details/6694452

    下面先贴上O(n2)的代码。

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <math.h>
     4 #include <algorithm>
     5 #include <string.h>
     6 
     7 using namespace std;
     8 
     9 const int MAXN = 1e5 + 10;
    10 int n = 1, a[MAXN], d[MAXN], dp[MAXN];
    11 
    12 int main()
    13 {
    14     int ans1 = 0, ans2 = 0; 
    15     while(scanf("%d", &a[n]) != EOF)
    16     {
    17         ++n; 
    18     }
    19     --n;
    20     for(int i = 1; i <= n; i++)
    21     {
    22         d[i] = 1;
    23         for(int j = 1; j < i; j++)
    24         {
    25             if(a[j] >= a[i])
    26             {
    27                 d[i] = max(d[i], d[j] + 1);
    28             }
    29         }
    30         if(ans1 < d[i])
    31         {
    32             ans1 = d[i];
    33         }
    34     }
    35     for(int i = 1; i <= n; i++)
    36     {
    37         dp[i] = 1;
    38         for(int j = 1; j < i; j++)
    39         {
    40             if(a[j] < a[i])
    41             {
    42                 dp[i] = max(dp[i], dp[j] + 1);
    43             }
    44         }
    45         if(ans2 < dp[i])
    46         {
    47             ans2 = dp[i];
    48         }
    49     }
    50     cout << ans1 << endl;
    51     cout << ans2 << endl;
    52     return 0;
    53 }

    下面是O(nlogn)的代码:

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <math.h>
     4 #include <algorithm>
     5 #include <string.h>
     6 
     7 using namespace std;
     8 
     9 const int MAXN = 1e5 + 10;
    10 int n = 1, a[MAXN], d[MAXN], dp[MAXN], ans1 = 0, ans2 = 0, mid, l, r;
    11 
    12 int main()
    13 { 
    14     while(scanf("%d", &a[n]) != EOF)
    15     {
    16         ++n; 
    17         continue;
    18     }
    19     --n;
    20     d[1] = a[1];
    21     dp[1] = a[1];
    22     ans1 = ans2 = 1;
    23     for(int i = 2; i <= n; i++)
    24     {
    25         if(a[i] <= d[ans1])
    26         {
    27             ans1++;
    28             d[ans1] = a[i];
    29         }
    30         else
    31         {
    32             l = 1, r = ans1;
    33             while(l < r)
    34             {
    35                 mid = (l + r) / 2;
    36                 if(a[i] > d[mid])
    37                 {
    38                     r = mid;
    39                 }
    40                 else
    41                 {
    42                     l = mid + 1;
    43                 }
    44             }
    45             d[l] = a[i];
    46         }
    47     }    
    48     
    49     for(int i = 2; i <= n; i++)
    50     {
    51         if(a[i] > dp[ans2])
    52         {
    53             ans2++;
    54             dp[ans2] = a[i];
    55         }
    56         else
    57         {
    58             l = 1, r = ans2;
    59             while(l < r)
    60             {
    61                 mid = (l + r) / 2;
    62                 if(a[i] <= dp[mid])
    63                 {
    64                     r = mid;
    65                 }
    66                 else
    67                 {
    68                     l = mid + 1;
    69                 }
    70             } 
    71             dp[l] = a[i];
    72         }
    73     }    
    74     cout << ans1 << endl;
    75     cout << ans2 << endl;
    76     return 0;
    77 }

    这段代码中有个细节需要注意,就是当二分查找时等号应该放在if条件里,还是放在else里面。例如,在求不升子序列时,因为希望a[i]和d[mid]相等,则希望把a[i]替换d[mid]右侧的数据,这样可以使d[mid]右侧的数据尽可能大,从而使序列更长。所以程序里面的两个比较条件非常重要,如果疏忽了会导致WA。

  • 相关阅读:
    010 排序: 冒泡 选择
    洛谷 P1540 机器翻译
    洛谷 P1011 车站
    周期串
    2019.03.29 大数据图解
    2019.03.29 算法解读
    2019.03.28 博客反省
    2019.03.27 常用的模块
    2019.03.25 git
    2019.03.25 Ajax三级联动
  • 原文地址:https://www.cnblogs.com/zealsoft/p/11329558.html
Copyright © 2011-2022 走看看