动态规划之最大递增子序列

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划之最大递增子序列相关的知识,希望对你有一定的参考价值。

参考技术A     能应用动态规划求解的问题必须满足两个要素:最优子结构和子问题重叠。

    最优子结构:如果一个问题的最优解包含其子问题的最优解,那么这个问题具有最优子结构。

    子问题重叠:如果递归算法反复求解相同的子问题,就称最优化问题具有重叠子问题性质。(如果一个问题在递归的每一步都会生成全新的子问题,那么这个问题通常适合使用分治法求解)

    对于求一个数组N的最大递增子序列的问题,如何把这个问题转化成适合使用动态规划求解的形式呢?

    构造数组DP[  ],使得DP[ i ] 为以N[ i ] 结尾的最大递增子序列的长度,那么对于N[ i ],DP[ i ]就等于MAX DP[ K ] + 1  , 其中 0<= k < i,N[ k ]  < N[ i ] ,也就是说N[ k ]是以N[ i ] 结尾的最大递增子序列的倒数第二个元素(所以必须满足 N[ k ] < N[ i ]),以N[ i ] 结尾的最大递增子序列的长度(也就是DP[ i ])是所有满足要求的 N[ k ]的DP[ k ] + 1中最大的一个。

    求解最大递增子序列的问题就转化为求解DP[  ] 的问题了,这个问题满足最优子结构和重叠子问题,最大递增子序列的长度就等于DP[  ] 中最大的元素。

动态规划之----最长递增子序列

一,问题描述

给定一个序列,求解它的最长 递增 子序列 的长度。比如: arr[] = {3,1,4,1,5,9,2,6,5}   的最长递增子序列长度为4。即为:1,4,5,9

 

二,算法分析

有两种方式来求解,一种是转化为LCS问题。即,首先对数组排序,将排序后的结果存储在辅助数组中。排序时间复杂度O(NlogN),排序后的数组与原数组组成了LCS(N,N)问题。解决LCS问题的时间复杂度为O(N^2),故整个算法的时间复杂度为O(N^2),空间复杂度为O(N)

 

另一种方式是直接用DP求解,算法如下:时间复杂度为O(N^2)

①最优子问题

设lis[i] 表示索引为 [0...i] 上的数组上的 最长递增子序列。初始时,lis[i]=1,注意,在DP中,初始值是很重要的,它是整个算法运行正确的关键而初始值 则可以 通过 画一个小的示例来 确定。

当 arr[i] > arr[j],lis[i] = max{lis[j]}+1 ;其中,j 的取值范围为:0,1...i-1

当 arr[i] < arr[j],lis[i] = max{lis[j]} ;其中,j 的取值范围为:0,1...i-1

 

②重叠子结构

从上面可以看出,计算 lis[i]时,需要计算 lis[j],其中 j < i,这说明有重叠子问题。借用网路中一张图如下:

技术分享图片
                     lis(4)           
                 /       |               lis(3)      lis(2)    lis(1)  
        /             /         
  lis(2)  lis(1)   lis(1) 
  /    
lis(1)
技术分享图片

而初始值 则可以 通过 画一个小的示例来 确定。

 

参考资料:

求解两个字符串的最长公共子序列

动态规划(3)-最长递增子序列

 

三,代码实现

错误版本1:

技术分享图片
 1     private static int lis(int[] arr, int length){
 2         int lis[] = new int[length];
 3         
 4         //init
 5         for(int i = 0; i < length; i++)
 6             lis[i] = 1;
 7         
 8         for(int i = 1; i < length; i++)
 9         {
10             for(int j = 0; j < i; j++)
11             {
12                 
13                 if(arr[i] > arr[j])
14                 {
15                     if(lis[j] + 1 > lis[i])
16                         lis[i] = lis[j] + 1;
17                 }
18                 else{
19                     if(lis[j] > lis[i])
20                         lis[i] = lis[j];
21                 }
22             }
23         }
24         return lis[length - 1];
25     }
技术分享图片

第13行if语句会导致bug,arr[i]要大于 j belongs to  0,1,...i-1 中所有的 arr[j]中的最大值,并且 lis[i] 是该最大值 arr[j] 所对应的 lis[j] +1,而不是某个其他的arr[j] 对应的 lis[j]+1

 

正确完整版本:

技术分享图片
public class LIS {
    public static int lis(int[] arr){
        if(arr == null || arr.length == 0)
            return 0;
        return lis(arr, arr.length);
    }
    
    private static int lis(int[] arr, int length){
        int lis[] = new int[length];
        
        //init
        for(int i = 0; i < length; i++)
            lis[i] = 1;
        
        for(int i = 1; i < length; i++)
        {
            for(int j = 0; j < i; j++)
            {
//                lis[i]=max{lis[i-1], lis[i-1]+1}
                if(arr[i] > arr[j] && lis[j] + 1 > lis[i])
                    lis[i] = lis[j] + 1;
            }
        }
        
        int max = lis[0];
        for(int i = 1; i < length; i++)
            if(max < lis[i])
                max = lis[i];
        return max;
    }
    
    public static void main(String[] args) {
        int[] arr = {3,1,4,1,5,9,2,6,5};
        int result = lis(arr);
        System.out.println(result);
    }
}
技术分享图片

以上是关于动态规划之最大递增子序列的主要内容,如果未能解决你的问题,请参考以下文章

力扣技巧之动态规划力扣300:最大递增子序列C++

算法之动态规划(最长递增子序列——LIS)

动态规划之最长递增子序列(LIS)

动态规划之----最长递增子序列

动态规划-最长递增子序列

关于用动态规划法求最大公共子序列的问题