package class47; // 整型数组arr长度为n(3 <= n <= 10^4),最初每个数字是<=200的正数且满足如下条件: // 1. 0位置的要求:arr[0]<=arr[1] // 2. n-1位置的要求:arr[n-1]<=arr[n-2] // 3. 中间i位置的要求:arr[i]<=max(arr[i-1],arr[i+1]) // 但是在arr有些数字丢失了,比如k位置的数字之前是正数,丢失之后k位置的数字为0 // 请你根据上述条件,计算可能有多少种不同的arr可以满足以上条件 // 比如 [6,0,9] 只有还原成 [6,9,9]满足全部三个条件,所以返回1种,即[6,9,9]达标 public class Code02_RestoreWays { public static int ways0(int[] arr) { return process0(arr, 0); } public static int process0(int[] arr, int index) { if (index == arr.length) { return isValid(arr) ? 1 : 0; } else { if (arr[index] != 0) { return process0(arr, index + 1); } else { int ways = 0; for (int v = 1; v < 201; v++) { arr[index] = v; ways += process0(arr, index + 1); } arr[index] = 0; return ways; } } } public static boolean isValid(int[] arr) { if (arr[0] > arr[1]) { return false; } if (arr[arr.length - 1] > arr[arr.length - 2]) { return false; } for (int i = 1; i < arr.length - 1; i++) { if (arr[i] > Math.max(arr[i - 1], arr[i + 1])) { return false; } } return true; } public static int ways1(int[] arr) { int N = arr.length; if (arr[N - 1] != 0) { return process1(arr, N - 1, arr[N - 1], 2); } else { int ways = 0; for (int v = 1; v < 201; v++) { ways += process1(arr, N - 1, v, 2); } return ways; } } // 如果i位置的数字变成了v, // 并且arr[i]和arr[i+1]的关系为s, // s==0,代表arr[i] < arr[i+1] 右大 // s==1,代表arr[i] == arr[i+1] 右=当前 // s==2,代表arr[i] > arr[i+1] 右小 // 返回0...i范围上有多少种有效的转化方式? public static int process1(int[] arr, int i, int v, int s) { if (i == 0) { // 0...i 只剩一个数了,0...0 return ((s == 0 || s == 1) && (arr[0] == 0 || v == arr[0])) ? 1 : 0; } // i > 0 if (arr[i] != 0 && v != arr[i]) { return 0; } // i>0 ,并且, i位置的数真的可以变成V, int ways = 0; if (s == 0 || s == 1) { // [i] -> V <= [i+1] for (int pre = 1; pre < 201; pre++) { ways += process1(arr, i - 1, pre, pre < v ? 0 : (pre == v ? 1 : 2)); } } else { // ? 当前 > 右 当前 <= max{左,右} for (int pre = v; pre < 201; pre++) { ways += process1(arr, i - 1, pre, pre == v ? 1 : 2); } } return ways; } public static int zuo(int[] arr, int i, int v, int s) { if (i == 0) { // 0...i 只剩一个数了,0...0 return ((s == 0 || s == 1) && (arr[0] == 0 || v == arr[0])) ? 1 : 0; } // i > 0 if (arr[i] != 0 && v != arr[i]) { return 0; } // i>0 ,并且, i位置的数真的可以变成V, int ways = 0; if (s == 0 || s == 1) { // [i] -> V <= [i+1] for (int pre = 1; pre < v; pre++) { ways += zuo(arr, i - 1, pre, 0); } } ways += zuo(arr, i - 1, v, 1); for (int pre = v + 1; pre < 201; pre++) { ways += zuo(arr, i - 1, pre, 2); } return ways; } public static int ways2(int[] arr) { int N = arr.length; int[][][] dp = new int[N][201][3]; if (arr[0] != 0) { dp[0][arr[0]][0] = 1; dp[0][arr[0]][1] = 1; } else { for (int v = 1; v < 201; v++) { dp[0][v][0] = 1; dp[0][v][1] = 1; } } for (int i = 1; i < N; i++) { for (int v = 1; v < 201; v++) { for (int s = 0; s < 3; s++) { if (arr[i] == 0 || v == arr[i]) { if (s == 0 || s == 1) { for (int pre = 1; pre < v; pre++) { dp[i][v][s] += dp[i - 1][pre][0]; } } dp[i][v][s] += dp[i - 1][v][1]; for (int pre = v + 1; pre < 201; pre++) { dp[i][v][s] += dp[i - 1][pre][2]; } } } } } if (arr[N - 1] != 0) { return dp[N - 1][arr[N - 1]][2]; } else { int ways = 0; for (int v = 1; v < 201; v++) { ways += dp[N - 1][v][2]; } return ways; } } public static int ways3(int[] arr) { int N = arr.length; int[][][] dp = new int[N][201][3]; if (arr[0] != 0) { dp[0][arr[0]][0] = 1; dp[0][arr[0]][1] = 1; } else { for (int v = 1; v < 201; v++) { dp[0][v][0] = 1; dp[0][v][1] = 1; } } int[][] presum = new int[201][3]; for (int v = 1; v < 201; v++) { for (int s = 0; s < 3; s++) { presum[v][s] = presum[v - 1][s] + dp[0][v][s]; } } for (int i = 1; i < N; i++) { for (int v = 1; v < 201; v++) { for (int s = 0; s < 3; s++) { if (arr[i] == 0 || v == arr[i]) { if (s == 0 || s == 1) { dp[i][v][s] += sum(1, v - 1, 0, presum); } dp[i][v][s] += dp[i - 1][v][1]; dp[i][v][s] += sum(v + 1, 200, 2, presum); } } } for (int v = 1; v < 201; v++) { for (int s = 0; s < 3; s++) { presum[v][s] = presum[v - 1][s] + dp[i][v][s]; } } } if (arr[N - 1] != 0) { return dp[N - 1][arr[N - 1]][2]; } else { return sum(1, 200, 2, presum); } } public static int sum(int begin, int end, int relation, int[][] presum) { return presum[end][relation] - presum[begin - 1][relation]; } // for test public static int[] generateRandomArray(int len) { int[] ans = new int[len]; for (int i = 0; i < ans.length; i++) { if (Math.random() < 0.5) { ans[i] = 0; } else { ans[i] = (int) (Math.random() * 200) + 1; } } return ans; } // for test public static void printArray(int[] arr) { System.out.println("arr size : " + arr.length); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } public static void main(String[] args) { int len = 4; int testTime = 15; System.out.println("功能测试开始"); for (int i = 0; i < testTime; i++) { int N = (int) (Math.random() * len) + 2; int[] arr = generateRandomArray(N); int ans0 = ways0(arr); int ans1 = ways1(arr); int ans2 = ways2(arr); int ans3 = ways3(arr); if (ans0 != ans1 || ans2 != ans3 || ans0 != ans2) { System.out.println("Oops!"); } } System.out.println("功能测试结束"); System.out.println("==========="); int N = 100000; int[] arr = generateRandomArray(N); long begin = System.currentTimeMillis(); ways3(arr); long end = System.currentTimeMillis(); System.out.println("run time : " + (end - begin) + " ms"); } }