最長上公升子串行LIS演算法實現

2021-09-26 03:55:23 字數 3205 閱讀 2485

介紹一:

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開始遍歷後面的元素即可。

下面是模板:

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

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

template

int lis(t a,int n)

return ans;

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

模板如下:

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

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

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

/**//begin_template_by_abilitytao_acm

template

int bsearch(t c,int n,t a)

}template

int lis(t a, int n)

return size;}

最長上公升子串行 LIS 演算法

開乙個棧,每次取棧頂元素top和讀到的元素temp做比較,如果temp top 則將temp入棧 如果temp top則二分查詢棧中的比temp大的第1個數,並用temp替換它。最長序列長度即為棧的大小top。這也是很好理解的,對於x和y,如果x y且stack y stack x 用stack x...

最長上公升子串行 LIS

題目 兩道題幾乎一樣,只不過對於輸入輸出的要求有所不同罷了。lis有兩種方法 一 第一種方法 時間複雜度為o n 2 狀態 dp i 區間為0 i的序列的lis 轉移方程 dp i max 1,dp k 1 0 k include include include include using name...

最長上公升子串行LIS

問題 給定n個整數a1,a2,a3,a4,a5,an,從左到右的順序盡量選出多個整數,組成乙個上公升子串行,相鄰元素不相等。例如 1,6,2,3,7,5,它的最長上公升子串行為 1,2,3,5。分析 剛開始想這個問題的時候我想用遞迴來解決問題,可是後來考慮到遞迴的時間複雜度高,就覺得不能使用,並且本...