zoukankan      html  css  js  c++  java
  • LIS LCS(学习笔记)

    LIS最长上升子序列

    一般求解LIS有两种做法,第一种直接(n^2)暴力枚举,a数组记录原序列,设(f[i])表示枚举到第i位(即以第i个元素结尾)的最长上升子序列的长度;

    for(int i=1;i<=n;i++)
    	f[i]=1;
    //这个初始化应该挺好理解吧;
    //初始看作每个元素自身构成一个序列,长度为1
    for(int i=1;i<=n;i++){
    	for(int j=1;j<i;j++){
        	if(a[j]<a[i]&&f[i]<f[j]+1)
            	f[i]=f[j]+1;
        }
        ans=max(ans,f[i]);
    }
    
    ans即为最终答案.
    

    这里主要是想介绍一下第二种(nlogn)的做法;设(f[i])表示长度为i的最长上升子序列的 末尾元素的最小值(这个设定好好理解一下),那么其实就是结合了贪心思想(很显然地,对于同样的长度为i的最长上升子序列,当前末尾元素越小的那个序列越有利于后面元素的加入);

    for(int i=1;i<=n;i++){
    	f[i]=inf;
    //把f[i]初始化为无穷大,是为了后面的比较大小
    }
    f[1]=a[1];
    //初始长度为1的序列的末尾元素就是a[1]
    int len=1;
    //len记录最长上升子序列的长度
    for(int i=2;i<=n;i++){
        int l=0,r=len,mid;
        if(a[i]>f[len])
        	f[++len]=a[i];
    //如果枚举到的当前元素a[i]
    //大于我们已经得到的最长上升子序列的末尾元素
    //那么直接长度+1,更新f[len];
        else{
    //否则,不断在已经确定的最长上升子序列中
    //查找一个能放下a[i]的合法位置
    //因为我们之前确定的是最长上升子序列(单调序列)
    //所以我们可以用二分查找
    //(这个二分查找不懂的,可以自己手动模拟一下)
            while(l<r){   
                mid=(l+r)/2;
                if(f[mid]>a[i])r=mid;
                else l=mid+1; 
            }
            f[l]=min(a[i],f[l]);//更新最小末尾 
        }
    }
    
    len即是最终的答案
    

    LCS最长公共子序列

    和LIS一样,求解LCS一般也有两种做法,一种是(n^2)的暴力做法,设(f[i][j])表示第一个序列(序列a)当前枚举到第i个位置,第二个序列(序列b)当前枚举到第j个位置,所得到的最长公共子序列的长度;

    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++){
       f[i][j]=max(f[i-1][j],f[i][j-1]);
       if(a[i]==b[j])
           f[i][j]=max(f[i][j],f[i-1][j-1]+1);
    }
    
    最后的答案就是f[n][m]
    

    不得不说明一下,(n^2)做法是对于任意两个长度分别为n的a序列和m的b序列;

    而以下(nlogn)的做法是对于两个序列长度都是n的且序列中的元素都是一样的(即a,b两个序列只是排列不一样);

    看到这里,可能有大佬会问:对于两个没有限制条件的序列怎么求它们的最长公共子序列呢?本蒟蒻只好回答:不会

    重点是第二种(nlogn)的做法,我们分析以下a,b两个序列的性质,它们的长度都是n并且序列内元素都是1~n,唯一的区别就是排列不同;

    根据这些性质,我们可以设置一个pla数组来记录a序列中的元素在b序列中的位置,也不难看出(算了,先不给结论)

    我们现在来看个简单的例子:

    A: 3 2 4 1 5

    B: 4 5 3 1 2

    这样一个简单的例子,我们能够直接看出,最长公共子序列的长度为2(即由4和5构成的序列)(或者3和2构成的序列)(或者3和1)

    那么按照上述方法设置一个pla数组来记录a序列中的元素在b序列中的位置,所以:

    pla[3]=3;

    pla[2]=5;

    pla[4]=1;

    pla[1]=4;

    pla[5]=2;

    我们来好好比较一下合法序列(4和5 或 3和2 或 3和1)在pla数组中的关系:

    pla[4]=1; pla[5]=2;(1<2)

    pla[3]=3; pla[2]=5;(3<5)

    pla[3]=3; pla[1]=4;(3<4)

    是不是看出了都是递增的关系,于是可以草率地得到这道题的结论,我们把a序列中的元素在b序列中的位置表示出来后(用pla数组记录),求解最长公共子序列相当于在pla数组中找一个最长上升子序列;

    于是我们就成功地转化为了上述求LIS的问题(所以懒蒟蒻就不写注释了)

    for(int i=1;i<=n;i++){
         pla[a[i]]=i;
         f[i]=inf;
    }
    int len=0;
    for(int i=1;i<=n;i++){
         int l=0,r=len,mid;
         if(pla[b[i]]>f[len])
            f[++len]=pla[b[i]];
         else {
            while(l<r){
               mid=(l+r)/2;
               if(f[mid]>pla[b[i]]) r=mid;
               else l=mid+1; 
            }
            f[l]=min(pla[b[i]],f[l]);
         }
    }
    最后的答案就是len
    
  • 相关阅读:
    NVelocity模板引擎用法之模板中直接调用类的方法
    GridView的编辑,更新,取消,删除等功能演示
    页面加载后检查用户登录状态,通过ajax实现
    通过ajax方式实现用户名存在性检查
    各个大神大公司的UED
    ASP.NET中的DataView用法详解
    周期三角脉冲信号的生成_Matlab实现
    课后题:冲激函数习题讨论
    [KeilC51] 有关出栈时sp原子性的疑虑
    [KeilC51]软件简单PWM波生成
  • 原文地址:https://www.cnblogs.com/PPXppx/p/9871708.html
Copyright © 2011-2022 走看看