1. 程式人生 > 其它 >分割陣列的最大值

分割陣列的最大值

連結

給定一個非負整數陣列 nums 和一個整數 m ,你需要將這個陣列分成 m 個非空的連續子陣列。

設計一個演算法使得這 m 個子陣列各自和的最大值最小。


  1. 未優化的動態規劃 O(n * n * m)
  2. 優化的動態規劃 O(n * m)
  3. 二分查詢 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;
    }
}
心之所向,素履以往 生如逆旅,一葦以航