競賽題目講解-【Northeastern Europe 2002, Far-Eastern Subregion】最長上升子序列

【Northeastern Europe 2002, Far-Eastern Subregion】最長上升子序列


Description
一個數的序列bi,當b1 < b2 < … < bS的時候,我們稱這個序列是上升的。對於給定的一個序列(a1, a2, …, aN),我們可以得到一些上升的子序列(ai1, ai2, …, aiK),這裏1 <= i1 < i2 < … < iK <= N。比如,對於序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。這些子序列中最長的長度是4,比如子序列(1, 3, 5, 8).
你的任務,就是對於給定的序列,求出最長上升子序列的長度。

Input
輸入的第一行是序列的長度N (1 <= N <= 1000)。第二行給出序列中的N個整數,這些整數的取值範圍都在0到10000。
Output
最長上升子序列的長度。

Sample Input

7
1 7 3 5 9 4 8

Sample Output

4

題目解析
這是一個動態規劃的題,按照常規,作者還是先介紹搜索(Time Limit Exceeded)的做法。首先的輸入無論是什麼做法都是一樣的,相信作者也不必講解了。接下來就是尋找答案了,這裏注意一個區別——子串是連續的,而子序列是可以斷開的。與尋找子串相同,子序列可以從原串的任意位置開始,於是我們用for循環枚舉每一個可能的子序列起點。然後我們需要寫一個函數,來求出以原串的某位置爲子序列的第一個元素的所有子序列中長度最長的子序列的長度。函數(flag)參數表如下:

int x:當前子序列末尾的元素在原串中的位置
int tot:當前子序列的長度

我們可以在進入函數時就更新答案(ans)的值爲當前找到的子序列的長度的最大值,也就是 ans=max(ans,tot);。由於是序列,那麼其中的元素在原串中就一定是從前往後排列的,因此我們用for循環從當前末尾在原串中的後一個位置枚舉到原串的尾部(x+1~n),來找出子序列的下一個元素。題目要求要“上升”,其實是給出了一個子序列的條件,於是我們在for循環查找下一個元素時需要判斷該元素是否比當前序列的末尾大(嚴格大於),也就保證了上升。
當所有的子序列都找出來後,搜索也就完畢了,那麼此時的ans也就是最終的答案了。

由於查找出所有子序列可能會有許多重複,因此容易超時。於是我們可以考慮進行記憶化,使得計算過的序列不再計算。因爲我們的搜索是要找到最長的一個子序列,我們可以用一個數組(f)來儲存,F[i]來表示在原序列中從開頭到第i個元素這一段序列裏以第i個元素爲末尾的子序列的最長長度。爲什麼是以元素i爲末尾呢?其實爲開頭還是末尾是不影響的,因爲既然每個元素都會作開頭,那麼必然所有元素都可以做末尾。既然F[i]的定義中已經包含了第i個元素,那麼子序列的長度至少爲1(只有第i個元素),即F[i]的最小值爲1。那麼我們可以以1爲初值,同樣用for循環找到最大的長度。我們也需要對函數進行改變,現在flag有返回值了,那麼需要把void改爲int;因爲F數組表示了長度,tot是不需要的,需要刪去。其他部分與搜索相同。


題外話
這個是作者從草稿箱裏翻出來寫的,可能有些晚,見諒!


程序樣例
Time Limit Exceeded-搜索:

/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
using namespace std;
int n,xl[1005]={},ans=1;
void flag(int x,int tot)
{
    ans=max(tot,ans);
    for(int i=x+1;i<=n;i++)
        if(xl[x]<xl[i])
            flag(i,tot+1);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&xl[i]);
    for(int i=1;i<=n;i++)
        flag(i,1);
    printf("%d\n",ans);
    return 0;
}

Accepted-動態規劃

/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
    int n,xl[1005]={},f[1005]={};
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d",&xl[i]);
    f[0]=1;
    for(int i=1;i<n;i++)
    {
        int s=0;
        for(int j=0;j<i;j++)
            if(xl[j]<xl[i])
                s=max(s,f[j]);
        f[i]=s+1;
    }
    sort(f,f+n);
    printf("%d\n",f[n-1]);
    return 0;
}

Accepted-記憶化搜索

/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
using namespace std;
int s[1005],f[1005],n,ans=1;
int flag(int x)
{
    if(f[x]) return f[x];
    else
    {
        f[x]=1;
        for(int i=0;i<x;i++)
            if(s[i]<s[x])
                f[x]=max(f[x],flag(i)+1);
        return f[x];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&s[i]);
    f[0]=1;
    for(int i=1;i<n;i++)
        ans=max(flag(i),ans);
    printf("%d\n",ans);
    return 0;
}

The End

Thanks for reading


發佈了76 篇原創文章 · 獲贊 44 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章