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 }
  • 相关阅读:
    设计模式-11-代理模式
    设计模式-10-装饰者
    设计模式-9-组合
    设计模式-8-适配器
    设计模式-7-原型模式
    设计模式-6-建造者
    设计模式-5-单例模式
    u-boot-1.1.6 设置新分区支持设备树
    u-boot-2014.10移植(8)重定位,支持NAND启动
    u-boot-2014.10移植(7)修改环境变量的存储位置
  • 原文地址:https://www.cnblogs.com/jily/p/6644934.html
Copyright © 2011-2022 走看看