package class_2022_05_3_week; // 来自京东 // 4.2笔试 // 给定一个数组arr,长度为N,arr中所有的值都在1~K范围上 // 你可以删除数字,目的是让arr的最长递增子序列长度小于K // 返回至少删除几个数字能达到目的 // N <= 10^4,K <= 10^2 public class Code02_RemoveNumbersNotIncreasingAll { // 暴力方法 // 为了验证 public static int minRemove1(int[] arr, int k) { return process1(arr, 0, new int[arr.length], 0, k); } public static int process1(int[] arr, int index, int[] path, int size, int k) { if (index == arr.length) { return lengthOfLIS(path, size) < k ? (arr.length - size) : Integer.MAX_VALUE; } else { int p1 = process1(arr, index + 1, path, size, k); path[size] = arr[index]; int p2 = process1(arr, index + 1, path, size + 1, k); return Math.min(p1, p2); } } public static int lengthOfLIS(int[] arr, int size) { if (size == 0) { return 0; } int[] ends = new int[size]; ends[0] = arr[0]; int right = 0; int l = 0; int r = 0; int m = 0; int max = 1; for (int i = 1; i < size; i++) { l = 0; r = right; while (l <= r) { m = (l + r) / 2; if (arr[i] > ends[m]) { l = m + 1; } else { r = m - 1; } } right = Math.max(right, l); ends[l] = arr[i]; max = Math.max(max, l + 1); } return max; } // arr[0...index-1]上,选择了一些数字,之前的决定! // len长度了!len = 3 : 1 2 3 // arr[index....]是能够决定的,之前的,已经不能再决定了 // 返回:让最终保留的数字,凑不足k长度的情况下,至少要删几个! public static int zuo(int[] arr, int index, int len, int k) { if (len == k) { return Integer.MAX_VALUE; } // 凑的(1...len)还不到(1...k) if (index == arr.length) { return 0; } // 没凑到 < k, 有数字! int cur = arr[index]; // 可能性1:保留 // 可能性2:删除 // 1...3 3 if (len >= cur || len + 1 < cur) { return zuo(arr, index + 1, len, k); } // 1..3 4 // len + 1 == cur // 可能性1:保留 int p1 = zuo(arr, index + 1, len + 1, k); // 可能性2:删除 int p2 = Integer.MAX_VALUE; int next2 = zuo(arr, index + 1, len, k); if(next2 != Integer.MAX_VALUE) { p2 = 1 + next2; } return Math.min(p1, p2); } // 正式方法 // 时间复杂度O(N*K) public static int minRemove2(int[] arr, int k) { int n = arr.length; int[][] dp = new int[n][k]; for (int i = 0; i < n; i++) { for (int j = 0; j < k; j++) { dp[i][j] = -1; } } return process2(arr, k, 0, 0, dp); } public static int process2(int[] arr, int k, int index, int range, int[][] dp) { if (range == k) { return Integer.MAX_VALUE; } if (index == arr.length) { return 0; } if (dp[index][range] != -1) { return dp[index][range]; } int ans = 0; if (arr[index] == range + 1) { int p1 = process2(arr, k, index + 1, range, dp); p1 += p1 != Integer.MAX_VALUE ? 1 : 0; int p2 = process2(arr, k, index + 1, range + 1, dp); ans = Math.min(p1, p2); } else { ans = process2(arr, k, index + 1, range, dp); } dp[index][range] = ans; return ans; } // 为了验证 public static int[] randomArray(int len, int k) { int[] arr = new int[len]; for (int i = 0; i < len; i++) { arr[i] = (int) (Math.random() * k) + 1; } return arr; } // 为了验证 public static void main(String[] args) { int N = 15; int K = 6; int testTime = 10000; System.out.println("测试开始"); for (int i = 0; i < testTime; i++) { int len = (int) (Math.random() * N) + 1; int k = (int) (Math.random() * K) + 1; int[] arr = randomArray(len, k); int ans1 = minRemove1(arr, k); int ans2 = minRemove2(arr, k); if (ans1 != ans2) { System.out.println("出错了!"); for (int num : arr) { System.out.print(num + " "); } System.out.println(); System.out.println("k : " + k); System.out.println("ans1 : " + ans1); System.out.println("ans2 : " + ans2); break; } } System.out.println("测试结束"); } }