import java.util.*; /** * NC91 最长上升子序列(三) * @author d3y1 */ public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * retrun the longest increasing subsequence * @param arr int整型一维数组 the array * @return int整型一维数组 */ public int[] LIS (int[] arr) { // return solution1(arr); return solution2(arr); } /** * 动态规划: 超时 * * dp[i]表示从前面到以第i个数字结尾的最长上升子序列的长度(arr[i]必须被选取) * * dp[i] = Math.max(dp[i], dp[j]+1) , arr[j] < arr[i] * * @param arr * @return */ private int[] solution1(int[] arr){ int len = arr.length; if(len == 0){ return new int[0]; } int[] dp = new int[len]; // 初始值 dp[0] = 1; // 最长上升子序列的长度 int max = 1; for(int i=1; i<len; i++){ dp[i] = 1; for(int j=0; j<i; j++){ if(arr[j] < arr[i]){ dp[i] = Math.max(dp[i], dp[j]+1); } } max = Math.max(max, dp[i]); } int[] result = new int[max]; for(int i=len-1,j=max; j>0; i--){ if(dp[i] == j){ result[--j] = arr[i]; } } return result; } /** * 贪心 + 二分 * @param arr * @return */ private int[] solution2(int[] arr){ int len = arr.length; // 表示从前面到以第i个数字结尾的最长上升子序列的长度(arr[i]必须被选取) int[] dp = new int[len]; // 最长上升子序列 单调增 int[] seq = new int[len+1]; seq[1] = arr[0]; dp[0] = 1; // 最长上升子序列的长度 int max = 1; for(int i=1; i<len; i++){ if(seq[max] < arr[i]){ seq[++max] = arr[i]; dp[i] = max; }else{ int left=1,right=max; // 上升子序列seq中二分查找最大的小于arr[i]的位置pos int pos = 0; while(left <= right){ int mid = (left + right) >> 1; if(seq[mid] < arr[i]){ pos = mid; left = mid + 1; }else{ right = mid - 1; } } // 替换 或 附加 seq[pos+1] = arr[i]; dp[i] = pos + 1; } } int[] result = new int[max]; for(int i=len-1,j=max; j>0; i--){ if(dp[i] == j){ result[--j] = arr[i]; } } return result; } }