淺析最長不下降子序列一題的方法
阿新 • • 發佈:2019-02-20
對於最長不下降子序列一題,問題描述如下:
有長度為N的序列:
A1 A2 …..An
求最長不下降子序列:Ai1,Ai2,,,,,Aik, 其中ai1<=ai2<=.....<=aik
求最長不下降子序列的長度
一看最長不下降子序列,最先想到的肯定是O(n²)的DP解法,當然,這裡還是寫下程式碼,加深理解:
var a,f:array[1..1000] of longint; n,i,j,ans:longint; function max(x,y:longint):longint; begin if x>y then exit(x) else exit(y); end; begin readln(n); for i:=1 to n do read(a[i]); for i:=n-1 downto 1 do for j:=i+1 to n do if a[i]<=a[j] then begin f[i]:=max(f[i],f[j]+1); ans:=max(ans,f[i]); end; writeln(ans+1); end.
(以下簡稱最長不下降子序列為LS)
但是,我們知道,其實這種方法是很笨的,因為有很多數明顯不能構成一個最優的序列,我們還拿它去比較,就是很多餘的。
但這不能構成一個最優序列是什麼意思呢?
假設當前構成的LS的長度為3,最後一位的數為7,那如果現在又加進一個數,也可以與前面的數構成長度為3的LS,那麼,到底是以6結尾好?還是7結尾好呢?毋庸置疑。。
當然是6.
當然是最後一位越小越好啊,這樣後面構成LS的長度才有可能更優,根據這一思路,我們就可以大概知道我們到底怎麼去優化LS了,那到底怎們實現呢?
所以我們可以多建一個f陣列表示以長度為i的最長不下降子序列的最後一個數是多少。每次增加一個數x,我們都需要去更新f陣列的值,找到一個剛好的下標i使得f[i]>x,則可把f[i+1]賦值為x.
很明顯這種方法是易證的.
程式碼:
var a,f:array[1..100000] of longint; n,i,k,len:longint; function find(len,n:longint):Longint; var l,r,mid:longint; begin l:=1; r:=len; while l<=r do begin mid:=(l+r) div 2; if f[mid]=n then exit(mid); if f[mid]>n then r:=mid-1 else l:=mid+1; end; exit(l); end; begin readln(n); for i:=1 to n do read(a[i]); len:=1; f[1]:=a[1]; for i:=2 to n do begin k:=find(len,a[i]); f[k]:=a[i]; if k>len then len:=k; end; writeln(len); end.
但是,對上面程式做一番思考之後,會發現,此方法只是使用於最長遞增子序列,即所輸入的數不能有相等的數,但我們要探討的是LS,所以,我們可以只需對上述程式做一個當相等的時候暴力求位置即可,但這種方法當最壞情況的時候效率還是有可能接近O(n²)的.
當然,我們也可以對二分改動一下,把L,r更新時有些數會重複的這些情況做相應的改變也可以 .最壞複雜度也是O(n²)