zoukankan      html  css  js  c++  java
  • 经典的导弹拦截问题——动态规划和一点形式化证明

    这是一道动态规划的经典问题,很多人的博客有写过,但是很多地方只有前半部分,后半部分题目有一些需要认真想想的点我也没见到令我满意的证明,不然我也不会再去写一次来说这个题目的。

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

      输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
    输入格式
      一行,为导弹依次飞来的高度
    输出格式
      两行,分别是最多能拦截的导弹数与要拦截所有导弹最少要配备的系统数
    样例输入
    389  207  155  300  299  170  158  65
    样例输出
    6
    2

    ————————PART I————————

    求数组的非增序列,动态规划来做。先斟酌一个选择,子结构的划分依据<采用当前点,不采用当前点>的背包思想,还是依据<必有当前点+后面每种可以的序列>?很容易明白第二种方法是好的,即是结果不是简单读d[0],也只需要一趟遍历找最大就可以得到结果。第一种其实没什么吸引力。

    首次得到最长序列长度后打印结果这些都OK。

    ————————PART II————————

    之后需要多次DP,并用结果来削减数组元素直至空,我用的是vector,所以获得要删除的元素位置后就可以简单的erase了。在动态规划时我维护一个parent数组记录每条路径,所以只要找到入口也就好办了。

    但是一直有一个疑问,当有两条长度相等的序列可供erase时,是不是会对下一趟遍历产生影响?这里需要证明一下才能得到算法的正确性。

    当前数组为A=<X1,X2,...,Xn>,DP获得两条路径A1=<Xa,...>,A2=<Xb,...>长度相同,A1和A2在最大区间[i, j]内元素数相同且没有相同元素(*)。假设之后对A-A1某一次动态规划获得的子序列长度小于来自A-A2的,所以这某一次DP后的序列中,(A-A1∩[i, j])∩(A-A2∩[i, j])=Ø,且Size(A-A1∩[i, j])<Size(A-A2∩[i, j]),那么在起始序列A中,[i, j]区间内,Size(A1∩[i, j])<Size(A2∩[i, j]),与假设*不符,所以之后某一次DP中,不会产生有差异的结果。所以算法不区分最长DP结果(们)的路径是安全的。

     1 #include <vector>
     2 #include <iostream>
     3 using namespace std;
     4 
     5 int dp(vector<int> &v)
     6 {
     7     int n = v.size();
     8     vector<int> d(n, 1);
     9     vector<int> p(n, -1);    //parent
    10     for (int i = n - 2; i >= 0; --i)
    11     {
    12         for (int j = i + 1; j < n; ++j)
    13         {
    14             //不分开讨论要不要采取这个数必须采取
    15             if (v[i] >= v[j] && d[i] < d[j] + 1)
    16             {
    17                 d[i] = d[j] + 1;
    18                 p[i] = j;
    19             }
    20         }
    21     }
    22     int mx = -1;
    23     int pos = 0;
    24     for (int i = pos; i < n; ++i)
    25     {
    26         if (d[i] > mx)
    27         {
    28             mx = d[i];
    29             pos = i;
    30         }
    31     }
    32     //整理数组
    33     vector<int> del;
    34     int i = pos;
    35     while (p[i] != -1)
    36     {
    37         del.insert(del.begin(), i);
    38         i = p[i];
    39     }
    40     del.insert(del.begin(), i);
    41     for (i = 0; i < del.size(); ++i)
    42     {
    43         v.erase(v.begin() + del[i]);
    44     }
    45     return mx;
    46 }
    47 int main()
    48 {
    49     vector<int> v;
    50     int h;
    51     while (cin >> h) v.push_back(h);
    52     int i = 1;
    53     for (;; ++i)
    54     {
    55         int ans = dp(v);
    56         if (i == 1) cout << ans << endl;
    57         if (v.size() == 0) break;
    58     }
    59     cout << i << endl;
    60 //    system("pause");
    61     return 0;
    62 }
  • 相关阅读:
    linux 命令——19 find (转)
    linux 命令——18 locate (转)
    linux 命令——17 whereis(转)
    linux 命令——16 which(转)
    linux 命令——15 tail (转)
    linux 命令——14 head (转)
    Java for LeetCode 038 Count and Say
    Java for LeetCode 037 Sudoku Solver
    Java for LeetCode 036 Valid Sudoku
    Java for LeetCode 035 Search Insert Position
  • 原文地址:https://www.cnblogs.com/jily/p/6644934.html
Copyright © 2011-2022 走看看