經典的導彈攔截問題——動態規划和一點形式化證明


這是一道動態規划的經典問題,很多人的博客有寫過,但是很多地方只有前半部分,后半部分題目有一些需要認真想想的點我也沒見到令我滿意的證明,不然我也不會再去寫一次來說這個題目的。

問題描述
  某國為了防御敵國的導彈襲擊,發展出一種導彈  攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以后每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的  導彈來襲。由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。

  輸入導彈依次飛來的高度(雷達給出的高度數據是不大於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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM