最長上公升子串行問題(lis,longest increasing subsequence)
有乙個長為n的數列a0,a1,a2,.......a(n-1)。求出這個序列中最長的上公升子串行的長度。
上公升子串行指的是對於任意的i
如 n=5
a=;則最長的上公升子串行長度為3,該子串行為(2,3,5)。
那如何得到狀態轉移方程呢?
先舉個例子。n=5,a=;dp[i]代表是以第i個數為結束的 最長上公升子串行(下面簡寫為lis)的長度。
當i=0時,a[0]=7,以7結尾的lis是,長度為1,即dp[0]=1;
當i=1時,a[1]=3,因為前面的7>3,以3結尾的lis是,所以dp[1]=1;
當i=2時,a[2]=4,以4結尾,前面就不能有a[0]=7了,但是可以由a[1]=3,所以以4為結尾的lis是,所以dp[2]=2;
當i=3時,a[3]=2,以1結尾的lis是,所以dp[3]=1;
當i=4時,a[4]=5,以5為結尾的上公升子串行(注意:不是最長上公升子串行)可以是,,。
而最長上公升子串行是,可以說是討論i=2的基礎在後面加上5.
從上面的分析可以得到
當討論i時,lis是前面i-1個最長上公升子串行加上a[i],前提是a[i]>a[j] (a[j]是前i-1個中lis的最後乙個)。
如討論i=2時,在原來的基礎上加上4.討論i=4時,在前面的lis基礎上加上5.
所以計算dp[i]的方法是從前面的dp[j] (ja[j]使得dp[i]=max(dp[j]+1),即以第i個結束的lis。
所以狀態轉移方程就出來了,dp[i]=max(1,dp[j]+1) (a[i]>a[j],i前面的1是代表前面都沒有a[i]>a[j]。如上例中的a[0]。
**實現:o(n^2)
#include#include#include#include#includeusing namespace std;
int dp[110000];//記錄以a[i]結束的最長子序列的長度
int a[110000];
int main()
len=max(len,dp[i]);//更新當前最長的上公升子串行
} printf("%d\n",len);
}return 0;
}
接下來是介紹lis的 o(nlogn)演算法,用於處理當n很大時的lis。
方法:最開始全部dp[i]的值都初始化為inf。然後由前到後逐個考慮數列的元素,對於每個a[j],如果i=0或者dp[i-1]
就用dp[i]=min(dp[i],aj)進行更新。最終找出使得dp[i]
因為對於已經有序的dp[0]=0)來說,前k+1個已經「填充」小於inf的數,即是當前長度已經記錄下來了,
然而對於即將要插入到序列的a[j]來說,如果a[j]>dp[i-1](i-1是當前dp數列最接近a[j]且大於a[i-1]的最小下標),將a[j]替換dp[i]。
則原來a[j]
對於已經有序的dp數列來說,在a[j]中找到乙個最後乙個小於a[j]的位置,可以用二分查詢的方法實現。
這樣複雜度就變成o(nlogn)。
例如 n=5,a=;
運算過程dp數列的變化:
**實現:
#include#include#include#include#includeusing namespace std;
const int inf=1e9;
int dp[110000];//記錄以a[i]結束的最長子序列的長度
int a[110000];
int main()
len=max(len,dp[i]);//更新當前最長的上公升子串行
}printf("%d\n",len);
}return 0;
}
動態規劃 最長上公升子串行
問題描述 乙個數的序列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 等等...
動態規劃 最長上公升子串行
動態規劃 儲存遞迴中間結果,減少遞迴次數 總時間限制 2000ms 記憶體限制 65536kb 描述 乙個數的序列 bi,當 b1 b2 bs的時候,我們稱這個序列是上公升的。對於給定的乙個序列 a1,a2 an 我們可以得到一些上公升的子串行 ai1,ai2 aik 這裡1 i1 i2 ik n。...
動態規劃 最長上公升子串行
總時間限制 2000ms 記憶體限制 65536kb 描述乙個數的序列bi,當b1 b2 bs的時候,我們稱這個序列是上公升的。對於給定的乙個序列 a1,a2,an 我們可以得到一些上公升的子串行 ai1,ai2,aik 這裡1 i1 i2 ik n。比如,對於序列 1,7,3,5,9,4,8 有它...