最長上公升子串行

2021-07-16 00:20:58 字數 3584 閱讀 3300

動態規劃--最長上公升子串行

lis(longest increasing subsequence)最長上公升(不下降)子串行,有兩種演算法複雜度為o(n*logn)和o(n^2)。在上述演算法中,若使用樸素的順序查詢在d1..dlen查詢,由於共有o(n)個元素需要計算,每次計算時的複雜度是o(n),則整個演算法的時間複雜度為o(n^2),與原來演算法相比沒有任何進步。但是由於d的特點(2),在d中查詢時,可以使用二分查詢高效地完成,則整個演算法時間複雜度下降為o(nlogn),有了非常顯著的提高。需要注意的是,d在演算法結束後記錄的並不是乙個符合題意的最長上公升子串行!演算法還可以擴充套件到整個最長子序列系列問題。

有兩種演算法複雜度為o(n*logn)和o(n^2)

o(n^2)演算法分析如下

(a[1]...a[n] 存的都是輸入的數)

1、對於a[n]來說,由於它是最後乙個數,所以當從a[n]開始查詢時,只存在長度為1的不下降子串行;

2、若從a[n-1]開始查詢,則存在下面的兩種可能性:

(1)若a[n-1] < a[n] 則存在長度為2的不下降子串行 a[n-1],a[n].

(2)若a[n-1] > a[n] 則存在長度為1的不下降子串行 a[n-1]或者a[n]。

3、一般若從a[t]開始,此時最長不下降子串行應該是按下列方法求出的:

在a[t+1],a[t+2],...a[n]中,找出乙個比a[t]大的且最長的不下降子串行,作為它的後繼。

4、為演算法上的需要,定義乙個陣列:

d:array [1..n,1..3] of integer;

d[t,1]表示a[t]

d[t,2]表示從i位置到達n的最長不下降子串行的長度

d[t,3]表示從i位置開始最長不下降子串行的下乙個位置

最長不下降子串行的o(n*logn)演算法

先回顧經典的o(n^2)的動態規劃演算法,設a[t]表示序列中的第t個數,f[t]表示從1到t這一段中以t結尾的最長上公升子串行的長度,初始時設f[t] = 0(t = 1, 2, ..., len(a))。則有動態規劃方程:f[t] = max (j = 1, 2, ..., t - 1, 且a[j] < a[t])。

現在,我們仔細考慮計算f[t]時的情況。假設有兩個元素a[x]和a[y],滿足

(1)x < y < t (2)a[x] < a[y] < a[t] (3)f[x] = f[y]

此時,選擇f[x]和選擇f[y]都可以得到同樣的f[t]值,那麼,在最長上公升子串行的這個位置中,應該選擇a[x]還是應該選擇a[y]呢?

很明顯,選擇a[x]比選擇a[y]要好。因為由於條件(2),在a[x+1] ... a[t-1]這一段中,如果存在a[z],a[x] < a[z] < a[y],則與選擇a[y]相比,將會得到更長的上公升子串行。

再根據條件(3),我們會得到乙個啟示:根據f的值進行分類。對於f的每乙個取值k,我們只需要保留滿足f[t] = k的所有a[t]中的最小值。設d[k]記錄這個值,即d[k] = min (f[t] = k)。

注意到d的兩個特點:

(1) d[k]的值是在整個計算過程中是單調不上公升的。

(2) d的值是有序的,即d[1] < d[2] < d[3] < ... < d[n]。

利用d,我們可以得到另外一種計算最長上公升子串行長度的方法。設當前已經求出的最長上公升子串行長度為len。先判斷a[t]與d[len]。若a[t] > d[len],則將a[t]接在d[len]後將得到乙個更長的上公升子串行,len = len + 1, d[len] = a[t];否則,在d[1]..d[len]中,找到最大的j,滿足d[j] < a[t]。令k = j + 1,則有d[j] < a[t] <= d[k],將a[t]接在d[j]後將得到乙個更長的上公升子串行,同時更新d[k] = a[t]。最後,len即為所要求的最長上公升子串行的長度。

在上述演算法中,若使用樸素的順序查詢在d[1]..d[len]查詢,由於共有o(n)個元素需要計算,每次計算時的複雜度是o(n),則整個演算法的時間複雜度為o(n^2),與原來的演算法相比沒有任何進步。但是由於d的特點(2),我們在d中查詢時,可以使用二分查詢高效地完成,則整個演算法的時間複雜度下降為o(nlogn),有了非常顯著的提高。需要注意的是,d在演算法結束後記錄的並不是乙個符合題意的最長上公升子串行!

這個演算法還可以擴充套件到整個最長子序列系列問題,整個演算法的難點在於二分查詢的設計,需要非常小心注意。

介紹二:

最長上公升子串行lis演算法實現  最長上公升子串行問題是各類資訊學競賽中的常見題型,也常常用來做介紹動態規劃演算法的引例,筆者接下來將會對poj上出現過的這類題目做乙個總結,並介紹解決lis問題的兩個常用演算法(n^2)和(nlogn).

問題描述:給出乙個序列a1,a2,a3,a4,a5,a6,a7....an,求它的乙個子串行(設為s1,s2,...sn),使得這個子串行滿足這樣的性質,s1

例如有乙個序列:1 7 3 5 9 4 8,它的最長上公升子串行就是 1 3 4 8 長度為4.

演算法1(n^2):我們依次遍歷整個序列,每一次求出從第乙個數到當前這個數的最長上公升子串行,直至遍歷到最後乙個數字為止,然後再取dp陣列裡最大的那個即為整個序列的最長上公升子串行。我們用dp[i]來存放序列1-i的最長上公升子串行的長度,那麼dp[i]=max(dp[j])+1,(j∈[1, i-1]); 顯然dp[1]=1,我們從i=2開始遍歷後面的元素即可。

下面是模板:

[cpp]view plain

copy

print

?//最長上公升子串行(n^2)模板

//入口引數:1.陣列名稱 2.陣列長度(注意從1號位置開始)

template

<

class

t>  

intlis(t a,

intn)  

dp[i]=m+1;  

if(dp[i]>ans)  

ans=dp[i];  

}  return

ans;  

}  

演算法2(nlogn):維護乙個一維陣列c,並且這個陣列是動態擴充套件的,初始大小為1,c[i]表示最長上公升子串行長度是i的所有子串中末尾最小的那個數,根據這個數字,我們可以比較知道,只要當前考察的這個數比c[i]大,那麼當前這個數一定能通過c[i]構成乙個長度為i+1的上公升子串行。當然我們希望在c陣列中找乙個盡量靠後的數字,這樣我們得到的上公升子串的長度最長,查詢的時候使用二分搜尋,這樣時間複雜度便下降了。

模板如下:

[cpp]view plain

copy

print

?//最長上公升子串行nlogn模板

//入口引數:陣列名+陣列長度,型別不限,結構體型別可以通過過載運算子實現

//陣列下標從1號開始。

/**/

/begin_template_by_abilitytao_acm  

template

<

class

t>  

intbsearch(t c,

intn,t a)  

}  template

<

class

t>  

intlis(t a, 

intn)  

return

size;  

}  

最長上公升子串行

問題描述 乙個數的序列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 等等...

最長上公升子串行

最長上公升子串行問題是各類資訊學競賽中的常見題型,也常常用來做介紹動態規劃演算法的引例,筆者接下來將會對poj上出現過的這類題目做乙個總結,並介紹解決lis問題的兩個常用 演算法 n 2 和 nlogn 問題描述 給出乙個序列a1,a2,a3,a4,a5,a6,a7.an,求它的乙個子串行 設為s1...

最長上公升子串行

最長上公升子串行問題 給出乙個由n個數組成的序列x 1.n 找出它的最長單調上公升子串行。即求最大的m和a1,a2 am,使得a1動態規劃求解思路分析 o n 2 經典的o n 2 的動態規劃演算法,設a i 表示序列中的第i個數,f i 表示從1到i這一段中以i結尾的最長上公升子串行的長度,初始時...