最长上升子序列

来源:互联网 发布:欧特克软件价格 编辑:程序博客网 时间:2024/05/29 16:58

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的大小。

原创粉丝点击