赞
踩
描述: 给定一个长度为 n 的数组 arr,求它的最长严格上升子序列的长度。 所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。 输入描述: 第一行输入一个正整数 n ,表示数组的长度 第二行输入 n 个整数表示数组的每个元素。 输出描述: 输出最长严格上升子序列的长度
LIS问题有三种解法:
解法一: 可以先将序列从小到大排序,然后求出排序后的序列和原来的序列的LCS长度就是最长上升子序列(LIS)长度 需要注意的是: 这种解法只能用于序列中没有重复元素的情况,因为要求子序列是严格上升的 解法二: step 1:用dp[i]表示到元素i结尾时,最长的子序列的长度,初始化为1,因为只有数组有元素,至少有一个算是递增。 step 2:第一层遍历数组每个位置,得到n个长度的子数组。 step 3:第二层遍历相应子数组求对应到元素i结尾时的最长递增序列长度,期间维护最大值。 step 4:对于每一个到i结尾的子数组,如果遍历过程中遇到元素j小于结尾元素,说明以该元素结尾的子序列加上子数组末尾元素也是严格递增的,因此转移方程为dp[i]=dp[j]+1. 前面两种解法的时间复杂度都一样,都是O(n^2),第三种解法的效率更高 解法三: 步骤(具体实现见代码注释): 1、准备一个辅助数组temp[]统计最长递增子序列的长度,逐个处理原来数组arr中的数字,例如处理的是arr[k] 2、如果arr[k]>temp[temp.length()-1],就将arr[k]加入到temp数组的末尾 3、如果arr[k]<temp[temp.length()-1],就替换temp[]数组中第一个大于arr[k]的数字。 该方法的时间复杂度为O(nlog2n)
代码实现:
-
-
- import java.util.*;
-
- public class Main {
-
- static int[] sortBefore; //记录原来的数组
-
- public static void main(String[] args) {
-
- Scanner scanner = new Scanner(System.in);
- int n = scanner.nextInt();
- int[] arr = new int[n];
- for (int i = 0; i < n; i++) {
- arr[i] = scanner.nextInt();
- }
- //保存原来的数组
- sortBefore = Arrays.copyOf(arr,arr.length);
- System.out.println("解法一的结果:" + firstSolve(arr));
- System.out.println("解法二的结果:" + secondSolve(sortBefore));
- System.out.println("解法三的结果:" + thirdSolve(sortBefore));
- }
-
- //解法一,这种解法只能用于序列中没有重复元素的情况,因为要求子序列是严格上升的
- public static int firstSolve(int[] arr){
- //给原来的数组排序
- Arrays.sort(arr);
- //求出两个数组的最长公共子序列的长度
- return LCS(sortBefore,arr);
- }
-
- //求最长公共子序列
- private static int LCS(int[] sortBefore, int[] arr) {
- int[][] dp = new int[arr.length+1][arr.length+1];
- for (int i = 1; i <= arr.length; i++) {
- for (int j = 1; j <= sortBefore.length; j++) {
- if (arr[i-1] == sortBefore[j-1])
- dp[i][j] = dp[i-1][j-1] + 1;
- else
- dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
- }
- }
- return dp[arr.length][arr.length];
- }
-
- //解法二
- public static int secondSolve(int[] arr){
- //dp[i]表示长度为i的序列中构成的最长上升子序列
- int[] dp = new int[arr.length];
- //设置数组长度大小的动态规划辅助数组
- Arrays.fill(dp, 1);
- int res = 0;
- for(int i = 1; i < arr.length; i++){
- for(int j = 0; j < i; j++){
- //可能j不是所需要的最大的,因此需要dp[i] < dp[j] + 1
- if(arr[i] > arr[j] && dp[i] < dp[j] + 1){
- //i点比j点大,理论上dp要加1
- dp[i] = dp[j] + 1;
- //找到最大长度
- res = Math.max(res, dp[i]);
- }
- }
- }
- return res;
- }
-
- //解法三
- public static int thirdSolve(int[] arr){
- //定义一个辅助数组
- int[] temp = new int[arr.length];
- //记录最终结果
- int len = 0;
- //将辅助数组中的值设为无穷小,方便判断
- Arrays.fill(temp,Integer.MIN_VALUE);
- //先将原来数组中的第一个元素放进temp中
- temp[0] = arr[0];
- for (int i = 1; i < arr.length; i++) {
- //找出temp中最后一个元素,并记录
- int k ,j;
- for (j = 0; j < temp.length; j++) {
- if (temp[j] == Integer.MIN_VALUE)
- break;
- }
- //k此时记录的是temp中最后一个元素的下标(不包括无穷小的元素)
- k = j - 1;
- if (arr[i] > temp[k]){
- temp[k+1] = arr[i];
- }else {
- //找出temp数组中第一个大于arr[i]的值并替换他
- //只需要循环k+1次即可
- for (int m = 0; m < k + 1; m++) {
- if (temp[m] > arr[i]){
- temp[m] = arr[i];
- break;
- }
- }
- }
- }
- //统计最长上升子序列长度
- for (int i = 0; i < temp.length; i++) {
- if (temp[i] == Integer.MIN_VALUE)
- break;
- len++;
- }
- //返回结果
- return len;
- }
- }
-
-
不了解LCS问题的码友可以看看我上一篇文章
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。