1. 程式人生 > >動態規劃--尋找最長遞減子序列

動態規劃--尋找最長遞減子序列

昨天C++課上留了三道題,除了C語言本身外都涉及了一些演算法。其中第二個問題是這樣的:

攔截導彈

某國為了防禦敵國的導彈襲擊,開發出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲,並觀測到導彈依次飛來的高度,請計算這套系統最多能攔截多少導彈。攔截來襲導彈時,必須按來襲導彈襲擊的時間順序,不允許先攔截後面的導彈,再攔截前面的導彈。

輸入

第一行,輸入雷達捕捉到的敵國導彈的數量kk<=25),

第二行,輸入k個正整數,表示k枚導彈的高度,按來襲導彈的襲擊時間順序給出,以空格分隔。

輸出

輸出只有一行,包含一個整數,表示最多能攔截多少枚導彈。

樣例輸入

8

300 207 155 300 299 170 158 65

樣例輸出

6

課堂上沒搞出來。老師說用到了動態規劃。好吧,高中沒學過資訊學競賽。不過沒關係,回去翻了翻《演算法導論》,基本算弄明白了。其實就是求一串數的最長遞減子序列。

問題關鍵在於,可能的情況組合很多,不適合窮舉。所以要利用動態規劃(很適合這類問題)。動態規劃把問題分成很多子問題,而子問題之間又有關係。子問題不是相互獨立的,子問題可能有重疊。這和分治法(典型的比如歸併排序)不一樣。動態規劃的基本想法在於:1.描述最優解結構,就是說最優解是什麼樣子的,符合什麼條件。2.遞迴定義最優解的值。3.自底向上計算最優解的值。4.由計算的值構造最優解(在這題裡沒要求,到第三部求出序列長就行了)。

這個問題中比如說要攔第i個導彈。截止到第i個導彈的最長攔截序列肯定包含了前面i-1個導彈中最長的序列。如果不是這樣,我們求出的截止到第i個導彈的截序列就不是最長的了。我們用陣列D[i]把截止到第i個導彈的最長攔截序列長度存起來。最後計算完i=k(導彈總數)時,整個問題的答案就是陣列D[i]的最大值。

如果用一個數組,把前一個最優解對應的導彈編號存下來,最後還可以把被擊落的導彈高度按編號輸出。

程式碼:

/*
    設陣列A存放導彈高度,D[i]是高度為A[i]是的最優解,S用於記錄最優解中導彈被擊落的次序
    最優解結構:設在第k個數處的最優解為D(k)。設當前位於第i個數,則在第i個數處的
                最優解D(i)為:在i之前攔下導彈最多的方法的解D(j)加上1。(因為把第i
                個也打下來了,所以加上1)。當然還要保證A[i]<=A[j]
    遞迴解:D(i)=D(j)+1。j是陣列A中位於A[i]左側,且大於等於A[i]的所有數中D值最大的
            那個。如果A[i]左側沒有找到大於等於它的數,則D[i]=1。
    最終結果:找到陣列D中最大的數,這個數就是結果。
*/
#include <cstdio>
int main(){
    int k;
    scanf("%d", &k);
    int A[k], D[k], S[k];

    for(int i=0; i<k; i++){
        scanf("%d", &A[i]);
        D[i]=0;
    }
    D[0]=1;

    for(int i=1; i<k; i++){
        bool found=false;   //是否找到i之前的最優解
        int last_good_solve=D[0];
        int good_position=0;//最優解的下標和值
        for(int j=0; j<i; j++)
            if(A[j]>=A[i] && D[j]>=D[good_position]){
                last_good_solve=D[j];
                good_position=j;
                found=true;
            }

        if(found){
            D[i]=1+D[good_position];
            S[i]=good_position;
        }
        else {
            D[i]=1;
            S[i]=good_position;
        }
    }

    int result=0;   int go_back;//用於向前找被擊落的導彈
    for(int i=0; i<k; i++){
        if(D[i]>result){
            result=D[i];
            go_back=i;
        }
    }

    printf("%d\n", result);

    for(int i=0; i<result; i++){
        printf("%d  ", A[go_back]);
        go_back=S[go_back];
    }//逆序輸出被擊落導彈的高度
}
圖片