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

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

posted @ 2012-07-27 00:58  celia01  阅读(2592)  评论(6编辑  收藏  举报