最长上升子序列(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};对此序列求最长递增子序列即可

不过这种转换好像没有太大的帮助

你可能感兴趣的:(log)