习题3-1:最长单调递增子序列
[算法设计与实验题解 page 63]
问题描述:
所谓子序列,就是在原序列里删掉若干个元素后剩下的序列,以字符串"abcdefg"为例子,去掉bde得到子序列"acfg"
现在的问题是,给你一个数字序列,你要求出它最长的单调递增子序列。
输入:
多组测试数据,每组测试数据第一行是n(1<=n<=10000),下一行是n个比1e9小的非负整数
输出:
对于每组测试数据输出一行,每行内容是最长的单调递增子序列的长度
样例输入:
5
1 2 4 8 16
5
1 10 4 9 7
9
0 0 0 1 1 1 5 5 5
样例输出:
5
3
3
我的Java 代码一:
/** * 算法时间复杂度是O(n^2) * 用数组b[0:n-1]记录以a[i],0<=i<n为结尾的元素的最长的递增子序列长度 * 序列a的最长的递增子序列的长度是max{b[i]}(0<=i<n),b[i]满足最优子结构,可以递归的定义: * b[0]=1;b[i]=max{b[k]}+1 (0<=k<i,a[k]<a[i]) */ import java.util.Scanner; public class LIS { public static int l; public static int maxl; public static int[] a; public static int[] b; public static void main(String[] args) { Scanner sin=new Scanner(System.in); while(sin.hasNext()){ l=sin.nextInt(); a=new int[l]; b=new int[l];//记住数组一定要初始化给定一个大小,不然后面赋值时会报空指针错误 for(int i=0;i<l;i++){ a[i]=sin.nextInt(); } maxl=dp(); System.out.println(maxl); } } private static int dp() { int i,j,k,t; t=0; b[0]=1; for(i=0;i<l;i++){ k=0; for(j=0;j<i;j++){ if(a[i]>a[j] && k<b[j]){ k=b[j]; } } b[i]=k+1; } for(i=0;i<l;i++){ if(t<b[i]){ t=b[i]; } } return t; } }
我的Java 代码二:
/** * 算法时间复杂度是O(nlog(n)) * 通过归纳假设得到问题变为:已知计算序列a的最长的递增子序列的长度k以及序列 * a中所有长度为k的递增子序列中的最小结尾元素b[k] */ import java.util.Scanner; public class LIS2 { public static int l; public static int maxl; public static int[] a; public static int[] b; public static void main(String[] args) { Scanner sin=new Scanner(System.in); while(sin.hasNext()){ l=sin.nextInt(); a=new int[l]; b=new int[l+1]; for(int i=0;i<l;i++){ a[i]=sin.nextInt(); } dp(); System.out.println(maxl); } } private static void dp() { int i,low,up,mid; b[1]=a[0]; maxl=1; for(i=1;i<l;i++){ if(a[i]>b[maxl]){//此时最大长度要加1,并且要改变最小元素值 maxl++; b[maxl]=a[i]; }else if(a[i]<b[1]){//如果比第一个元素还要小,那么就换一下最小的元素值 b[1]=a[i]; }else{//这种情况下要找到合适的位置将a[i]插入到b中的某个位置 low=1; up=maxl; // while(low!=up-1){//原书的写法 // mid=(low+up)/2; // //System.out.println(low+" "+up+" "+mid); // //System.out.println(b[mid]+" "+a[i]); // if(b[mid]<=a[i]){//找到满足b[j-1]<=a[i]<b[j]的j(=up) // low=mid; // }else{ // up=mid; // } // //System.out.println(low+" "+up+" "+mid); // } // b[up]=a[i]; while(low<up){//另一种写法,更加符合二分查找 mid=(low+up)/2;//注意条件:low等于up时就要退出了 //System.out.println(low+" "+up+" "+mid); //System.out.println(b[mid]+" "+a[i]); if(b[mid]<=a[i]){//找到满足b[j-1]<=a[i]<b[j]的j(=up) low=mid+1; }else{ up=mid-1; } //System.out.println(low+" "+up+" "+mid); } b[up]=a[i]; } } } }
其他的资源:
可供参考的资料:
1.这道题目的两种解题算法总结博客:http://old.blog.edu.cn/user2/43845/archives/2006/1115309.shtml
2.国家集训队论文 李睿的论文:〈二分法和统计研究〉:http://wenku.baidu.com/view/313fbcd126fff705cc170a18.html
3.论坛中的其他解题方法:http://yzfy.org/dis/listpost.php?tid=101