输出包含多行,第一行包括一个整数,代表信封的个数n。接下来n行,每行两个整数和,代表信封的长度和宽度。
输出包括一行,代表信封最多嵌套多少层。
9 3 4 2 3 4 5 1 3 2 2 3 6 1 2 3 2 2 4
4
从里到外分别是{1,2},{2,3},{3,4},{4,5}。
2 1 4 4 1
1
时间复杂度,空间复杂度。
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); int[][] pairs = new int[n][2]; for(int i = 0; i < n; i++){ String[] params = br.readLine().split(" "); pairs[i][0] = Integer.parseInt(params[0]); pairs[i][1] = Integer.parseInt(params[1]); } System.out.println(maxEnvelopes(pairs)); } private static int maxEnvelopes(int[][] envelopes) { if (envelopes.length == 0) return 0; int n = envelopes.length; // 按w升序,按h降序,然后求h的最长上升子序列 Arrays.sort(envelopes, new Comparator<int[]>() { public int compare(int[] e1, int[] e2) { if (e1[0] != e2[0]) return e1[0] - e2[0]; else return e2[1] - e1[1]; } }); // 这样就不可能在一个宽度值中选择到两个不同h的信封 int[] dp = new int[n]; // dp[i]表示在选择信封i的情况下,前i个信封所构成的最长上升序列长度 Arrays.fill(dp, 1); int maxLen = 1; for(int i = 1; i < n; i++){ for(int j = 0; j < i; j++){ if(envelopes[j][1] < envelopes[i][1]) { // 此时就可以在dp[j]的基础上选择第i个信封,信封数+1,尝试前面不同的j,看哪个能使dp[i]最大 dp[i] = Math.max(dp[i], dp[j] + 1); } } // 确定了dp[i]的值,更新dp数组的最大值 maxLen = Math.max(maxLen, dp[i]); } return maxLen; } }整体思路是尝试每个高度作为结尾时能够获得的最长递增子序列长度,但是对于某一个高度envelopes[i][1],我们需要把它接在一个小于它高度的信封envelopes[j](j<i)后面,并且信封j必须是所有高度小于信封i的信封中dp[j]最大的那个。要把它找出来的话我们就必须对i的左边进行穷举,从而使得算法的时间复杂度过高。
import java.lang.String; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine()); int[][] pairs = new int[n][2]; for(int i = 0; i < n; i++){ String[] params = br.readLine().split(" "); pairs[i][0] = Integer.parseInt(params[0]); pairs[i][1] = Integer.parseInt(params[1]); } System.out.println(maxEnvelopes(pairs)); } private static int maxEnvelopes(int[][] envelopes) { if(envelopes.length == 0) return 0; int n = envelopes.length; // 按w升序,按h降序,然后求h的最长上升子序列,这样就不可能在一个宽度值中选择到两个不同h的信封 Arrays.sort(envelopes, new Comparator<int[]>() { public int compare(int[] e1, int[] e2) { if (e1[0] != e2[0]) return e1[0] - e2[0]; else return e2[1] - e1[1]; } }); int[] dp = new int[n]; int[] ends = new int[n]; dp[0] = 1; ends[0] = envelopes[0][1]; int tail = 0; // ends数组中有效区域的最后一个元素 int maxLen = 1; for(int i = 1; i < n; i++){ int index = lowerbound(ends, 0, tail, envelopes[i][1]); // 寻找第一个h大于等于当前信封高度的位置 if(index > tail){ tail ++; ends[index] = envelopes[i][1]; }else{ ends[index] = envelopes[i][1]; } dp[i] = index + 1; maxLen = Math.max(maxLen, dp[i]); } return maxLen; } private static int lowerbound(int[] arr, int L, int R, int target) { int left = L, right = R, idx = R + 1; while(left <= right){ int mid = left + ((right - left) >> 1); if(arr[mid] < target){ left = mid + 1; }else{ idx = mid; right = mid - 1; } } return idx; } }这样通过二分查找,就将之前经典动态规划中对左边的枚举行为的时间复杂度O(N)降低至O(logN),从而使得算法的整体达成题目要求的时间复杂度O(NlogN),空间复杂度O(N)。