這是一道動態規划的經典問題,很多人的博客有寫過,但是很多地方只有前半部分,后半部分題目有一些需要認真想想的點我也沒見到令我滿意的證明,不然我也不會再去寫一次來說這個題目的。
問題描述
某國為了防御敵國的導彈襲擊,發展出一種導彈 攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以后每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的 導彈來襲。由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。
輸入導彈依次飛來的高度(雷達給出的高度數據是不大於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 }
