zoukankan      html  css  js  c++  java
  • 求最长非降(递增)子序列LIS的长度,及注意事项

    非降序列(Increasing Sequence)例如:

    (1) 完全递增型序列:S={1,3,6,7,9}

    (2) 部分存在等于的序列:S={1,3,3,6,9}

    S的非降子序列:由原序列S的元素组成的(且保持元素之间的顺序不变的)组成的序列。

    例如S={5,4,7,1,8,6}的子序列包括:

    1个元素的:{5},{4},{7},{1},{8},{6} 等6个子序列

    2个元素的:{5,7},{5,8},{5,6},{4,7},{4,8},{4,6},{7,8},{1, 8},{1,6} 等

    3个元素的:{5,7,8},{4,7,8}

    最长非降子序列(Longest Increasing Sequences,LIS),是S的所有非降子序列中包含元素最多的那些子序列,例如上述序列的最长子序列为{5,7,8}和{4,7,8}

    而最长非降子序列的长度就是最长非降子序列的元素个数,例如上例中的最长非降子序列的长度为3。

    求解序列S的最长非降子序列的长度通常采用动态规划算法:

    设d(i)为前i个元素的最长非降子序列的长度,则d(i)=max{ d(j)+1}, 其中 0<=j<=i,且S[j]<S[i]。

    然后写出类似这样的代码:

    public static void LIS(int []S)
        {
            int []d=new int[S.length];
            
            for(int i=0;i<S.length;i++)
            {
                d[i]=1;
                for(int j=0;j<i;j++)
                {
                    if(S[j]<=S[i] && d[j]+1<d[i])
                    {
                        d[i]=d[j]+1;
                    }    
                }
            }
            
            for(int i=0;i<S.length;i++)
            {
                System.out.print(d[i]+" ");
            }
        }

    然后,调用该LIS函数,求解。如

    int[]seq={5,3,4,8,6,7};
    LIS(seq);

    会输出:1 1 2 3 3 4 ,代表包含前1个元素的LIS长度为1,包含前2个元素的子序列的LIS长度为1,包含前3个元素的子序列的LIS长度为2,。。。

    需要注意的是:上述递推公式d(i)=max{ d(j)+1}, 其中 0<=j<=i,且S[j]<S[i],只适用于以S[i]作为结尾元素的LIS序列。

    而如果去掉以S[i]作为结尾元素这个条件,就不能这么求解。

    例如int [ ]seq={5,4,7,1,8,6 }, 用上述程序求解,会输出1 1 2 1 3 2 (即d[3]=1 ,d[5]=2,分别代表包含前4个元素子序列的LIS长度为1,包含前6个元素子序列的LIS长度为2)。但其实包含前4个元素的子序列(即{5,4,7,1})的LIS的长度应该是2,即,d[3]=2, LIS={5,7}或{4,7}, 而 d[5] 应该是3。

    问题出在,上述的程序要求LIS以S[i]作为结尾,即LIS的最大元素为S[i],这时d[i]=max{d[j],其中0<=j<=i }是没错的,但如果去掉这个限制条件,就用上面的程序求解就会出错。

    那么,如何求解序列S的最长非降子序列的长度(不要求以S的最后一个元素作为LIS的最大元素)?

    这通常需要转化为最长公共子序列(LCS)的问题。即:序列S的LIS= 将S排序后的有序序列S' 与 原序列S 的最长公共子序列。

    例如,S={5,4,7,1,8,6 } 的LIS为{ 4,7,8 }或{ 5,7,8 },长度为3

    对S排序后的S'={ 1,4,5,6,7,8}, S‘与 S的最长公共子序列就是{4, 7, 8 }和{ 5,7,8 }

    所以,如何求解序列S的最长非降子序列的长度,首先需要进行排序,然后求LCS的长度即可。

    示例代码:

    public static void main(String[]args)
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int []peaches=new int[n];
        for(int i=0;i<n;i++)
        {
            peaches[i]=sc.nextInt();
        }
        int []copy=peaches.clone();
        quickSort(peaches,0,n-1);
        int lisLength=LCS(peaches,copy)
        System.out.println("LIS length is"+lisLength);
    }
    
    public static void quickSort(int []arr,int start,int end)
    {
        if(start>=end)
            return;
        int i=start;
        int j=end;
        int key=arr[i];
        while(i<j)
        {
            while(i<j&&key<=arr[j])
            {
                j--;
            }
            arr[i]=arr[j];
            while(i<j&&key>arr[i])
            {
                i++;
            }
            arr[j]=arr[i];
        }
        arr[i]=key;
        quickSort(arr,start,i-1);
        quickSort(arr,i+1,end);
    }
    
    public static int LCS(int[]a,int[] b)
    {
        int [][]c=new int[a.length+1][b.length+1];
        for(int i=1;i<=a.length;i++)
        {
            for(int j=1;j<=b.length;j++)
            {
                if(a[i-1]==b[j-1])
                    c[i][j]=c[i-1][j-1]+1;
                else
                    c[i][j]=Math.max(c[i-1][j], c[i][j-1]);
            }
        }
        return c[a.length][b.length];
    }

    如何求LCS的长度?看这里

  • 相关阅读:
    hdu 4027 Can you answer these queries? 线段树
    ZOJ1610 Count the Colors 线段树
    poj 2528 Mayor's posters 离散化 线段树
    hdu 1599 find the mincost route floyd求最小环
    POJ 2686 Traveling by Stagecoach 状压DP
    POJ 1990 MooFest 树状数组
    POJ 2955 Brackets 区间DP
    lightoj 1422 Halloween Costumes 区间DP
    模板 有源汇上下界最小流 loj117
    模板 有源汇上下界最大流 loj116
  • 原文地址:https://www.cnblogs.com/aaronhoo/p/7368082.html
Copyright © 2011-2022 走看看