分割陣列的最大值
阿新 • • 發佈:2021-10-14
給定一個非負整數陣列 nums 和一個整數 m ,你需要將這個陣列分成 m 個非空的連續子陣列。
設計一個演算法使得這 m 個子陣列各自和的最大值最小。
- 未優化的動態規劃 O(n * n * m)
- 優化的動態規劃 O(n * m)
- 二分查詢 O(n * log(sum - max))
心之所向,素履以往 生如逆旅,一葦以航class Solution { private static int[] getPreSum(int[] nums) { int[] ret = new int[nums.length]; ret[0] = nums[0]; for (int i = 1; i < nums.length; ++i) { ret[i] = ret[i - 1] + nums[i]; } return ret; } private static int getRegionSum(int[] preSum, int l, int r) { return l == 0 ? preSum[r] : preSum[r] - preSum[l - 1]; } public static int splitArray0(int[] nums, int m) { if (nums == null || nums.length == 0) { return 0; } int n = nums.length; int[] preSum = getPreSum(nums); int[][] dp = new int[n][m + 1]; dp[0][1] = nums[0]; for (int i = 1; i < n; ++i) { dp[i][1] = dp[i - 1][1] + nums[i]; } for (int i = 1; i < n; ++i) { for (int j = 2; j <= Math.min(m, i + 1); ++j) { dp[i][j] = Integer.MAX_VALUE; for (int k = i; k >= j - 1; --k) { dp[i][j] = Math.min(dp[i][j], Math.max(dp[k - 1][j - 1], getRegionSum(preSum, k, i))); } } } return dp[n - 1][m]; } public static int splitArray1(int[] nums, int m) { if (nums == null || nums.length == 0) { return 0; } int n = nums.length; int[] preSum = getPreSum(nums); int[][] dp = new int[n][m + 1]; int[][] choose = new int[n][m + 1]; dp[0][1] = nums[0]; for (int i = 1; i < n; ++i) { dp[i][1] = dp[i - 1][1] + nums[i]; } for (int i = 1; i < n; ++i) { for (int j = 2; j <= Math.min(m, i + 1); ++j) { dp[i][j] = Integer.MAX_VALUE; for (int k = i; k >= j - 1; --k) { dp[i][j] = Math.min(dp[i][j], Math.max(dp[k - 1][j - 1], getRegionSum(preSum, k, i))); } } } for (int i = 1; i < n; ++i) { for (int j = Math.min(m, i + 1); j >= 2; --j) { int down = Math.max(choose[i - 1][j], j - 1); int up = Math.min(j == Math.min(m, i + 1) ? i : choose[i][j + 1], i); dp[i][j] = Integer.MAX_VALUE; for (int k = down; k <= up; ++k) { if (dp[i][j] > Math.max(dp[k - 1][j - 1], getRegionSum(preSum, k, i))) { dp[i][j] = Math.max(dp[k - 1][j - 1], getRegionSum(preSum, k, i)); choose[i][j] = k; } } } } return dp[n - 1][m]; } public static int splitArray(int[] nums, int m) { if (nums == null || nums.length == 0) { return 0; } int sum = 0; int max = nums[0]; for (int num : nums) { sum += num; max = Math.max(max, num); } int left = max, right = sum, ret = 0; while (left <= right) { int mid = (left + right) >> 1; if (search(nums, m, mid)) { ret = mid; right = mid - 1; } else { left = mid + 1; } } return ret; } private static boolean search(int[] nums, int m, int aim) { int count = 1; int sum = 0; for (int num : nums) { sum += num; if (sum > aim) { count++; sum = num; } if (count > m) { return false; } } return true; } }