最长上升子序列

1.题目描述
传送门
2.code(java1.7版本)
n方的代码

import java.util.*;
public class Main{
    public static int N;
    public static void main(String[]args){
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
            N=sc.nextInt();
            int[] ans=new int[N];
            for(int i=0;i<N;i++){
                ans[i]=sc.nextInt();
            }
            System.out.println(help(ans));

        }
    }
    public static int help(int[] ans){
        int[] dp=new int[N];
        dp[0]=1;
        for(int i=1;i<N;i++){
            dp[i]=1;
            for(int j=0;j<i;j++){
                if(ans[j]<ans[i])dp[i]=Math.max(dp[i],dp[j]+1);
            }
        }
        int max=0;
        for(int i=0;i<N;i++){
            max=Math.max(max,dp[i]);
        }
        return max;
    }
}

nlogn的代码

import java.util.*;
public class Main{
    static int[] ans=new int[10002];
    static int[] dp=new int[10002];
    static int len=0;
    static int N;
    public static void main(String[]args){
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
            N=sc.nextInt();
            for(int i=0;i<N;i++){
                ans[i]=sc.nextInt();
            }
            dp[0]=ans[0];
            len=1;
            for(int i=1;i<N;i++){
                if(ans[i]>dp[len-1]){
                     dp[len++]=ans[i];
                }
                else{
                    int pos=LIS(i);
                    dp[pos]=ans[i];
                }
            }
            System.out.println(len);
        }
    }
    public static int LIS(int i){
        int left,right,mid;
        left=0;
        right=len-1;
        while(left<right){
            mid=left+(right-left)/2;
            if(dp[mid]>=ans[i])right=mid;
            else{
                left=mid+1;
            }
        }
        return left;
    }
}

3.解题思路
运用一个辅助的数组dp来保存原数组以每个数结尾最长的子序列长度,假设存在一个序列a[1..9] = 2 1 5 3 6 4 8 9 7,我们可以很轻松的看出来它的LIS长度为5。但是如果一个序列太长后,就不能直接看出来了!
对于n方代码的思路
我们定义一个序列dp,它的长度和原数组的长度是一样长的,然后令 i = 1 to 9 逐个考察这个序列。首先,直接赋予dp[0]=1,因为第一个数结尾它的最长子序列一定为1。接着判断第2到第9个数,用i表示2-9,j表示0-i-1,如果a[j]小与a[i],这个时候需要更新dp[i]的值,将它赋予为max(dp[i],dp[j]+1),取最大的一个。这样循环完毕之后,dp数组保存的便是原数组中结尾的对应的最长子序列的长度,遍历一遍即可找出总的最长子序列长度。
对于nlogn代码的思路
对于n方复杂度的代码就是不断的对应更新dp数组的值,最后取出dp中最大的那个值即可;而对于nlogn的代码,更新dp的规则有一些变化,dp数组不一一对应保存原数组中每个结尾的最长子序列长度,描述它的更新规则,我们以下面的实例来看:
原数组为a[1..9] = 2 1 5 3 6 4 8 9 7,定义一个记录长度的变量len,下面开始填充dp数组。
第一步:直接赋值dp[0]=a[0],len为1,因为这个时候第一个元素,以它结尾的子序列长度肯定为1。这时的状态为dp 2,len=1.
第二步:考察a[1]这个数,这个时候直接看dp数组里面是有序的,先比较a[1]和dp[len-1]大小,这时a[1]比较小,则利用二分查找的方法,在dp数组里面查找刚好比a[1]大一步的数的下标,为0,这时直接将dp[0]更新为a[1].这时的状态dp 1,len 1.
第三步:考察a[2]这个数,还是比较a[2]和dp[len-1]大小,这时a[2]比较大,这直接赋值dp[1]为a[2],len加1.这时的状态,dp 1 5,len 2.
数据太多,偷个懒。。。
第四步到第九步,更新dp的规则还是按照第二步和第三步一样,最后的状态为dp 1 3 4 8,len 4.
最后整个序列的子序列长度即为len的大小。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值