最長上升子序列(O(n^2)與O(nlogn)+二分)最長公共子序列


最長上升子序列(LIS)

最長上升子序列是最基本的dp問題,以前一直都只寫過O(n^2)的解法,現在終於有時間整理一下了。

把poj上的幾道最長上升子序列的水題又重新做了一下,主要有163125333903

方法一:O(n^2)

dp[i]:表示處理到第i個位置,序列的最長上升子序列末尾為i的長度; a[]數組存儲原序列

dp[i] = max{dp[j]+1},a[i]>a[j],0≤j≤i

方法二:O(nlogn)

方法一中求dp[i]時需要O(n)的復雜度,其實我們最后只需要知道最大的dp[j]+1就可以了,所以如果能夠維護一個單調的數組,從而即可實現二分查找。

dp[]和a[]與方法一中具有同樣意義,再添加一個maxv[]數組,maxv[l]表示包含的最長上升子序列長度為l時,末尾的最小的數的值,舉個例子:

a[]:      1、2、3、-1123、1

dp[]:    1、2、3、1234、2

處理完第N位最后的maxv就會是:-1、1、2、3,毋庸置疑,maxv肯定是遞增的,而且找到最小的值就保證了對於固定長度l時,可以直接從存儲在maxv中的數得到最優解(呃,不知道怎么說清楚了),maxv在求解dp的過程中也是要不斷更新的。

如果最后要輸出解的情況的話,那么maxv中就不能簡單的存儲固定長度對應的原序列的最小值了,而要記錄下編號,好以后利用pre數組遞歸回去,而此時滿足單調的數組就不是maxv[i]與i的關系了,而是a[maxv[i]]與i的關系了,更新maxv數組時也要注意。

不輸出解(poj 3903):

View Code

輸出解(沒有找到題號,自己寫的):

View Code

最長公共子序列(LCS)

最長公共子序列的基本轉移方程為:d[i][j] = max{d[i'][j']}+1, p[i] = q[j],i'>i,j'>j

基本的典型題是POJ 1159,附代碼

View Code
 1 /*也可進一步用滾動數組優化*/
 2 
 3 #include<iostream>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<cstring>
 8 using namespace std;
 9 short dp[5010][5010];
10 
11 char s1[5010], s2[5010];
12 int main(){
13     int n, m, i, j, k;
14     scanf("%d",&n);
15     cin>>s1;
16     for(i=0;i<n;i++){
17         s2[i] = s1[n-1-i];
18     }
19     memset(dp,0,sizeof(dp));
20 
21     for(i=1;i<=n;i++){
22         for(j=1;j<=n;j++){
23             if(s1[i-1]==s2[j-1]){
24                 dp[i][j] = dp[i-1][j-1] + 1;
25             }
26             else{
27                 dp[i][j] = max(dp[i][j-1],dp[i-1][j]);
28             }
29         }
30     }
31     printf("%d\n",n-dp[n][n]);
32     return 0;
33 }

據說,LIS也又O(nlogn)的做法,使用靜態二叉樹(黑書上又說),目前還不會

另外,最長上升子序列(LCS)最長公共子序列(LIS)其實可以互相轉換。

LCS -> LIS:把序列排序后與原序列找最長公共子序列

LIS -> LCS:設有序列A,B。記序列A中各個元素在B中的位子(降序排列),然后按在A中的位置依次列出然后求A的最長遞增子序列。

例如:
A串位置             1 2 3 4                             B串位置:        1 2 3 4

                  A串: 4 3 5 2                                              B串: 2 5 4 3



A串在B串--位置  3 4 2 1  因為B串已經順序了,只要求出這行的最長遞增子序列 就是最長公共子序列的長度了。
  例如:有A={a,b,a,c,x},B={b,a,a,b,c,a}則有a={6,3,2},b={4,1},c={5};x=/;(注意降序排列)

然后按A中次序排出{a(6,3,2),b(4,1),a(6,3,2),c(5),x()}={6,3,2,4,1,6,3,2,5};對此序列求最長遞增子序列即可

不過這種轉換好像沒有太大的幫助


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM