(lis longest increasing subsequence)給定乙個數列,從中刪掉任意若干項剩餘的序列叫做它的乙個子串行,求它的最長的子串行,滿足子串行中的元素是單調遞增的。
例如給定序列,答案是3,因為和就是長度最長的兩個單增子序列。
處看此題,怎麼做? 萬能的列舉?列舉全部2^n個子序列,找出最長的,固然可以,就是複雜度太高。我們為什麼要列舉呢?因為要知道取了哪些數,其實我們只需要考慮上乙個數和取了幾個數就可以了吧?因為單增的意思是比前乙個數大,我們要加入這個數的時候,只考慮它比之前加入的最後乙個數大就可以了。而最長的意思是數的個數最多,我們只要知道數的總個數就可以了,沒必要知道具體有哪些數。
讓我們嘗試一下用動態規劃的思考辦法。首先設定數列是a1, a2, a3…an,為了方便我們加入一項a0=-∞,後面我們將發現這會給我們帶來極大的方便。int f[i]表示以第i個數結尾的最長單調子串行的長度, 那麼我們看一下加入ai之前的最後乙個數是aj,顯然j < i並且aj < ai,我們有f(i) = f(j) + 1,因為往後面延長了一項嘛。那根據這個式子,我們顯然應該選擇最大的f(j),才能讓f(i)最大。
於是我們有了遞推關係f(i) = max + 1,光有了遞推關係還不夠,初值呢? f(0) = 0,並且我們加入了a0=-∞,這樣對每個i > 0,j總是存在的,大不了就達到下標0了嘛。
偽**:
f[0] = 0;顯然這個演算法的時間複雜度是o(n^2),空間複雜度是o(n)。老生常談的問題,如何找到這樣乙個最長的子串行?記錄決策的辦法總是可以的我們記錄一下使得f(i)最大的j。最終結果是max,我們從這個i值一項一項不斷找到前面的j即可……for i = 1 to n do
f[i] = 0;
for j = 0 to i – 1 do
f[i] = max(f[i], f[j] + 1)
endfor
endfor
更好的演算法?
事實上這個題有時間複雜度更低的演算法。仍然以為例子,我們想像考慮5的時候,之前有兩個長度為2的子串行和,那麼哪個更「好」呢?顯然後者更好,因為3比6小,以3結尾的序列更容易在後面接上乙個數。那麼我們記錄到第i個數之前每個長度的單調子串行中「最好」的那個的最後乙個數的大小,考慮把當前這個數接在**就好了。事實上,我們的意思是在每個長度的單調子串行中選乙個代表,這個代表就是其中「最好」的那個(讓最後一項盡可能小),而我們可以歸納的證明,不同長度的「最好」單調子串行的最後一項是隨著長度而單調遞增的。這是因為,我們每次都試圖把乙個數加到它能接的那個最長的子串行後面,其實也是加到了它能加的末尾最大的子串行上。
那麼問題明了了,開始我們只有乙個長度為0的單調子串行,末尾大小認為是-∞。假設目前我們記錄了f[0],f[1],f[2]…f[m]表示目前單調子串行的最長長度是m,我們考慮ai接到**,我們要找到小於ai的最大那一項,我們把它接到那個序列的後面。因為f是單調遞增的,換句話說,我們找到x= max, 把ai接到f[x]後面,得到f[x + 1] = ai,注意這樣的x一定存在,因為f[0] = -∞。如果我們找到的x < m,則我們實際上更新了長度為(x + 1)的子串行的最後一項,因為顯然有f[x + 1] >= ai,我們把ai換過去,至少不會變差,這也正式我們儲存每個長度「最好」的單調子串行的初衷。如果x == m,則實際上我們把子序列的長度(種類數)擴充套件到了(m + 1)。
最終結果是什麼呢?是f那個列表的長度,也就是最終變化後的m值。
如果我們迴圈乙個乙個地看,這裡就有o(m)的時間複雜度,但是因為有單調性的存在,我們可以利用二分查詢演算法來找到這樣的x,所以這裡時間複雜度是o(logm),因為m<=n,我們這裡可以認為每次找到x的時間複雜度是o(logn),那麼對於每個ai我們都如此做的時間複雜度就是o(nlogn)了。
最後,我們來提供輸入輸出資料,由你來寫一段程式,實現這個演算法,只有寫出了正確的程式,才能繼續後面的課程。
輸入
第1行:1個數n,n為序列的長度(2 <= n <= 50000)輸出第2 - n + 1行:每行1個數,對應序列的元素(-10^9 <= s[i] <= 10^9)
輸出最長遞增子串行的長度。輸入示例
851輸出示例6824
510
5
1注意:這裡的二分查詢要留心,如果寫成這樣會死迴圈:deffind(y,l,r):
2if (l==r) and (f[l]
3returnl4
elif l>r:
5return l-1
6else
:7 mid=int((l+r) / 2)
8if f[mid]>=y:
9return find(y,l,mid-1)
10else:11
return find(y,mid+1,r)
12 n=int(input())
13 a=[-10000000000]
14 f=[-10000000000]
15for i in
range(n):
16 x=int(input())
1718
19 m=0
20for i in range(1,n+1):
21 x=find(a[i],0,m)
22 f[x+1]=a[i]
23if x==m:
24 m+=1
2526
print(m)
1因為,如果r= l+1且f[l]deffind(y,l,r):
2if (l==r) and (f[l]
3returnl4
else
:5 mid=int((l+r) / 2)
6if f[mid]>=y:
7return find(y,l,mid-1)
8else:9
return find(y,mid,r)
動態規劃 最長遞增子串行
給出序列 1 2 3 4 2 5 3 4 a 1 1,a 2 2,a 7 3,a 8 4 求其最長的遞增子串行,以上最長遞增子串行為 1 2 3 4 5 問題細分 初始化條件f 1 1,序列只有1個長度即為1 f 2 a 2 與下標小於2的比較,即a 1 比較,a 2 a 1 因此更新f 2 f 1...
動態規劃 最長遞增子串行
給定乙個無序的整數陣列,找到其中最長上公升子串行的長度 例項 輸入 10,9,2,5,3,7,101,18 輸出 4 解釋 最長的上公升子串行為 2,3,7,101 長度為4說明 可能會有多種最長上公升子串行的和,只需要輸出對應長度即可 演算法的時間複雜度應為o n2 首先,dp陣列的定義如下 dp...
動態規劃 最長遞增子串行
最長遞增子串行是動態規劃中最經典的問題之一,該問題描述的是在乙個已知序列中,取出若干元素 不必連續 組成乙個新的序列,子串行的各個數先後順序保持不變,且對子序列中的任意下標x令dp i 表示以a i 作為末尾的最長遞增子串行的長度。於是,通過設定這麼乙個陣列,最長遞增子串行的長度便是陣列dp中的最大...