動態(tài)規(guī)劃 | 最長上升子序列

1.問題描述
描述
一個數(shù)的序列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).

你的任務(wù)烁涌,就是對于給定的序列,求出最長上升子序列的長度酒觅。
輸入
輸入的第一行是序列的長度N (1 <= N <= 1000)撮执。第二行給出序列中的N個整數(shù),這些整數(shù)的取值范圍都在0到10000舷丹。
輸出
最長上升子序列的長度抒钱。
樣例輸入
7
1 7 3 5 9 4 8
樣例輸出
4


2.解題思路

此題用動態(tài)規(guī)劃來解決
第一種方法
1)找子問題
一個上升子序列中最右邊的那個數(shù),稱為該子序列的“終點”颜凯。
即可轉(zhuǎn)化成:
“求以ak(k=1,2,3谋币,.....,N)為終點的最長上升子序列的長度”

雖然這個子問題和原問題形式上并不完全一樣症概,但是只要這N個子問題都解決了蕾额,那么這N個子問題的解中,最大的那個就是整個問題的解彼城。

2)確定狀態(tài)
子問題只和一個變量(數(shù)字的位置相關(guān))诅蝶,因此序列中數(shù)的位置k就是“狀態(tài)”,而狀態(tài)k對應(yīng)的“值”募壕,就是以ak作為“終點”的最長上升子序列的長度调炬。
狀態(tài)一共有N個。

3)找出狀態(tài)轉(zhuǎn)移方程
maxLen(k)表示以ak作為“終點”的最長上升子序列的長度

那么:
83B2174318194BFBA80E83B1A23DFC51.jpg

maxLen(k)的值舱馅,就是在ak左邊缰泡,“終點”數(shù)值小于ak,且長度最大的那個上升子序列的長度再加1代嗤。因為ak左邊任何“終點”小于ak的子序列棘钞,加上ak后就能形成一個更長的子序列缠借。

程序如下:時間復雜度為O(N^2)

#include <iostream>
#define MaxSize 1001
using namespace std;
int MaxInArray(int * arr,int n);
int main(){
    int n;
    int MaxLen[MaxSize];//以i為終點的最長上升子序列的長度
    int array[MaxSize];
    cin >> n;
    for(int i =1;i<=n;++i){
        cin >> array[i];
        MaxLen[i] = 1;//賦初值 
    }
    for(int i=1;i<=n;++i)
//每次求以第i個數(shù)為終點的最長子序列的長度
        for(int j =1;j<=i;++j)
//察看以第j個數(shù)為終點的最長上升子序列
            if(array[i] > array[j])
                MaxLen[i] = max(MaxLen[i],MaxLen[j]+1);
    cout << MaxInArray(MaxLen,n);
    return 0;
} 
int MaxInArray(int *arr,int n){
    int Max = arr[1];
    for(int i=2;i<=n;++i)
        if(Max < arr[i])
            Max = arr[i];
    return Max;
}

第二種方法(時間復雜度為O(nlogn))
后來在遇到一道LIS的題,用上面O(nlogn)的過不了宜猜,然后了解到下面這種解法:
我們定義一個最小的有序序列l(wèi)ens[]泼返,其長度為len。
用題目給的1 7 3 5 9 4 8來舉例宝恶。
我們定義一個數(shù)組a[7]={1,7,3,5,9,4,8}
a[1] = 1符隙,我們把1放進lens[1]趴捅,然后序列l(wèi)ens的長度len+1垫毙,len = 1
a[2] = 7,a[2]>lens[1],我們把7放進lens[2]拱绑,然后序列l(wèi)ens的長度len+1综芥,len = 2
a[3] = 3,a[3]<lens[2],因為序列l(wèi)ens存放的是最小的有序序列猎拨,所以我們把lens[2]替換成
a[3]膀藐,此時lens[2] = 3,因為此時只是替換红省,并沒有使lens變長额各,所以len不變
a[4] = 5,a[4]>lens[2],我們把5放進lens[3]吧恃,然后序列l(wèi)ens的長度len+1虾啦,len = 3
a[5] = 9,a[5]>lens[3],我們把9放進lens[4]痕寓,然后序列l(wèi)ens的長度len+1傲醉,len = 4
a[6] = 4,a[6]<lens[3],我們把lens[3]替換成a[6],此時lens[3] = 4呻率,因為此時只是替換,并沒有使lens變長,所以len不變
a[7] = 8,a[7]<lens[4],我們把lens[4]替換成a[7]掖肋,此時lens[4] = 8会宪,因為此時只是替換,并沒有使lens變長元践,所以len不變
最終len為4挪丢,和我們所要求的長度結(jié)果是一致的,但是此時的有序序列不一定就是我們的最長上升子序列卢厂,此方法只能求長度乾蓬,但是不能用來求真正的最長上升子序列

經(jīng)過上面的例子,我們可以得出

如果a[i]>lens[len]慎恒,lens[++len] = a[i]
不然則從lens這有序序列中找出一個下界x(找到一個最大的x滿足len[x]<a[i])任内,然后將lens[x]替換為a[i]撵渡,此時查找下界用二分查找,即可使整個算法的時間復雜度為O(nlogn)

改進版程序代碼:

#include <iostream>
using namespace std;
int arr[100000];
int search(int l,int r,int num,int a[]);
int LIS(int n);
int main(){
    int n;
    cin >> n;
    for(int i=1;i<=n;++i)
        cin >> arr[i];
    cout << LIS(n) <<endl;
    return 0;
}
int search(int l,int r,int num,int a[]){
    int mid;
    while(l<=r){
        mid = (l+r)>>1;
        if(a[mid]<=num)
            l = mid+1;
        else
            r = mid - 1;
    }
    return l;
}
int LIS(int n){
    int * lens = new int [n+1];
    int len = 1;
    lens[1] = arr[1];
    for(int i = 2;i<=n;++i){
        if(arr[i]>lens[len])
            lens[++len] = arr[i];
        else{
            int p = search(1,len,arr[i],lens);
            lens[p] = arr[i]; 
        }
    }
    return len;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末死嗦,一起剝皮案震驚了整個濱河市趋距,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌越除,老刑警劉巖节腐,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異摘盆,居然都是意外死亡翼雀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門孩擂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狼渊,“玉大人,你說我怎么就攤上這事类垦”芬兀” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵蚤认,是天一觀的道長米苹。 經(jīng)常有香客問我,道長砰琢,這世上最難降的妖魔是什么蘸嘶? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮氯析,結(jié)果婚禮上亏较,老公的妹妹穿的比我還像新娘。我一直安慰自己掩缓,他們只是感情好雪情,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著你辣,像睡著了一般巡通。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舍哄,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天宴凉,我揣著相機與錄音,去河邊找鬼表悬。 笑死弥锄,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播籽暇,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼温治,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了戒悠?” 一聲冷哼從身側(cè)響起熬荆,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绸狐,沒想到半個月后卤恳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡寒矿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年突琳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劫窒。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡本今,死狀恐怖拆座,靈堂內(nèi)的尸體忽然破棺而出主巍,到底是詐尸還是另有隱情,我是刑警寧澤挪凑,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布孕索,位于F島的核電站,受9級特大地震影響躏碳,放射性物質(zhì)發(fā)生泄漏搞旭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一菇绵、第九天 我趴在偏房一處隱蔽的房頂上張望肄渗。 院中可真熱鬧,春花似錦咬最、人聲如沸翎嫡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惑申。三九已至,卻和暖如春翅雏,著一層夾襖步出監(jiān)牢的瞬間圈驼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工望几, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绩脆,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像靴迫,于是被迫代替她去往敵國和親祈坠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自:https://blog.csdn.net/George__Yu/article/details/75896...
    laochonger閱讀 1,386評論 0 1
  • 描述 給定一個整數(shù)序列矢劲,找到最長上升子序列(LIS)赦拘,返回LIS的長度。 說明 最長上升子序列的定義:最長上升子序...
    6默默Welsh閱讀 690評論 1 0
  • 給定一個整數(shù)序列芬沉,找到最長上升子序列(LIS)躺同,返回LIS的長度。 說明 最長上升子序列的定義: 最長上升子序列問...
    Arnold134777閱讀 1,004評論 0 0
  • 問題描述 一個數(shù)的序列bi丸逸,當b1 < b2 < ... < bS的時候蹋艺,我們稱這個序列是上升的。對于給定的一個序...
    指尖極光閱讀 355評論 0 0
  • 算法簡述 最長上升子序列(Longest Increasing Subsequence, 簡稱LIS)是dp中比較...
    xiaoshua閱讀 7,285評論 0 5