1. 程式人生 > >8596 最長上升子序列(動規)

8596 最長上升子序列(動規)

8596 最長上升子序列

時間限制:300MS  記憶體限制:1000K
提交次數:255 通過次數:118 

題型: 程式設計題   語言: C++;C;VC;JAVA

Description

A numeric sequence of ai is ordered if a1 < a2 < ... < aN. 
Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK), where 1 <= i1 < i2 < ... < iK <= N. 
For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, e. g., (1, 7), (3, 4, 8) and many others. 
All longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8). 

Your program, when given the numeric sequence, must find the length of its longest ordered subsequence. 



輸入格式

There are several test cases. Every test case includes two lines. 
The first line contains the length of sequence N. The second line contains the elements of sequence - N integers in the range from 0 to 10000 each, 
separated by spaces. 1 <= N <= 1000 
When N is 0, it indicates test to end. 


輸出格式

Output must contain a single integer for every test case ---- the length of the longest ordered subsequence of the given sequence. 


輸入樣例

7
1 7 3 5 9 4 8
6
1 8 3 6 5 9
5
1 2 3 4 5
0


輸出樣例

4
4
5


提示

一,對輸入字串的處理

注意:這道題和其他題的輸入輸出不同,這題是接收多組資料而非單組,以0來判別結束.
大家在接受資料的時候,不要用(c=getchar())!='\n'諸如此類一個字元一個字元接受,然後判斷是否是回車符號來結束一行的輸入,
這樣的方式在你本機執行不會有問題,但OJ系統中會有錯誤,無法輸出結果,因為測試平臺行末並非'\n'字元。
這裡接受資料用scanf的%d或%s,或cin等,會自動判別結束字元的,你就不要在你程式裡專門去判別或吸收回車字元。

對於最後一組資料輸入為0表示結束,只要判斷接受的第一個字元是否為0且字串長度為1就結束,無須去處理回車字元。

輸入的序列可以用整型陣列或字串陣列儲存。


二,演算法的動態規劃思想

考慮採用動態規劃演算法,針對每個元素,以該元素結尾的最長有序子序列作為子問題,
計算出每個子問題的最大長度用“表”記錄下來。先寫出遞推關係式再程式設計實現。

設f(i)表示:從左向右掃描過來直到以a[i]元素結尾的序列,可獲得的最長上升子序列的長度,且最長上升子序列包含a[i]元素(1<=i<=n)。

(這裡大家思考一下,為何要這樣假設子問題和子問題最優解f(i)?有同學問:為何最長上升子序列要包含a[i]元素(1<=i<=n)?
因為你所設的子問題要和更下一級子問題關聯起來。
如果長度為i序列的最長上升子序列中沒有規定包含a[i]元素,那如何和其字首的最長上升子序列問題關聯起來呢,那樣顯然是比較麻煩的。)

f(i)是從f(1),f(2), ……到f(i-1)中找最大的一個值,再加1,或者就是1。
這主要得看a[i]這個元素能否加入到之前已經獲得的最長上升子序列當中去,
如果能加入,是之前已獲得的最長上升子序列長度加1;
如果不能加入,就開始一個新的上升子序列,長度為1。
最後,所要求的整個序列的最長上升子序列長度為 max{ f(i): 1<=i<=n }

f(i)的遞推公式如下:
(1)f(i) = 1              當i=1;
(2)f(i) = max{f(j)+1}    當i>1, 對某個前面的j(1<=j<i), 有a[j]<a[i]];
(3)f(i) = 1              當i>1, 對任意j(1<=j<i), 都有a[j]>=a[i]

例子,對於序列:4  2  6  3  1  5  2
   i = 1  2  3  4  5  6  7
a[i] = 4  2  6  3  1  5  2
f(i) = 1  1  2  2  1  3  2

這裡max{f(i)}=3為原問題所求的最長上升子序列的長度。

效率分析:
f(i)的計算不超過O(n),因此,整個演算法為O(n^2)。


作者

zhengchan

我的實現程式碼:

#include <iostream>

using namespace std;

// 找出陣列中最大值
int maxL(int f[],int n){
    int temp =0;
    for(int i = 0; i < n; i++){
        if(f[i] > temp){

            temp = f[i];
        }
    }

    return temp;
}

// 求上升子序列長度
int LOSubstr(int a[], int f[], int n){

    int i,j,k;

    f[0] = 1;
    for(i = 1;i < n; i++){

        k = 0;
        for(j = 0; j < i; j++){
            // 與i之前每一個比較
            if(a[j] <= a[i] && k < f[j]){

                k = f[j];// k為當前除自己外的子序列長度
            }
        }
        f[i] = k + 1;// 加上自己
    }

    return maxL(f, n);
}

/*

7
1 7 3 5 9 4 8
6
1 8 3 6 5 9
5
1 2 3 4 5
0

*/

int main(){

    while(true){

        int n;
        cin >> n;
        if(n == 0){// 退出
            break;
        }

        int a[n],f[n];

        for(int i = 0; i < n; i++){
            // 讀入序列
            cin >> a[i];
        }

        cout << LOSubstr(a,f,n) << endl;
    }

    cout << endl;
    return 0;
}