410. 分割数组的最大值

给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。

设计一个算法使得这 m 个子数组各自和的最大值最小。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/split-array-largest-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

未优化的动态规划

class Solution {

    private static int[] getPreSum(int[] nums) {
        int n = nums.length;

        int[] preSum = new int[n];
        preSum[0] = nums[0];
        for (int i = 1; i < nums.length; ++i) {
            preSum[i] = preSum[i - 1] + nums[i];
        }
        return preSum;
    }

    private static int getRegionSum(int[] preSum, int l, int r) {
        return l == 0 ? preSum[r] : preSum[r] - preSum[l - 1];
    }

    public static int splitArray(int[] nums, int m) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        // dp[i][j] [0, i] 分为 j份

        int n = nums.length;

        int[] preSum = getPreSum(nums);
        int[][] dp = new int[n][m];

        // [0, i] 分为 1份
        for (int i = 0; i < n; ++i) {
            dp[i][0] = getRegionSum(preSum, 0, i);
        }

        for (int i = 1; i < n; ++i) {
            for (int j = 2; j <= Math.min(m, i + 1); ++j) {
                dp[i][j - 1] = Integer.MAX_VALUE;
                for (int k = j - 1; k <= i; ++k) {
                    dp[i][j - 1] = Math.min(dp[i][j - 1], Math.max(dp[k - 1][j - 2], getRegionSum(preSum, k, i)));
                }
            }
        }

        return dp[n - 1][m - 1];
    }
}

优化的动态规划

class Solution {

    private static int[] getPreSum(int[] nums) {
        int n = nums.length;

        int[] preSum = new int[n];
        preSum[0] = nums[0];
        for (int i = 1; i < nums.length; ++i) {
            preSum[i] = preSum[i - 1] + nums[i];
        }
        return preSum;
    }

    private static int getRegionSum(int[] preSum, int l, int r) {
        return l == 0 ? preSum[r] : preSum[r] - preSum[l - 1];
    }

    public static int splitArray(int[] nums, int m) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        // dp[i][j] [0, i] 分为 j份

        int n = nums.length;

        int[] preSum = getPreSum(nums);
        int[][] dp = new int[n][m];
        int[][] choose = new int[n][m];

        // [0, i] 分为 1份
        for (int i = 0; i < n; ++i) {
            dp[i][0] = getRegionSum(preSum, 0, i);
        }

        for (int i = 1; i < n; ++i) {
            for (int j = Math.min(m, i + 1); j >= 2; --j) {
                dp[i][j - 1] = Integer.MAX_VALUE;

                int down = Math.max(j - 1, choose[i - 1][j - 2]);
                int up = Math.min(i, j == Math.min(m, i + 1) ? i : choose[i][j]);

                for (int k = down; k <= up; ++k) {
                    int next = Math.max(dp[k - 1][j - 2], getRegionSum(preSum, k, i));
                    if (dp[i][j - 1] > next) {
                        dp[i][j - 1] = next;
                        choose[i][j - 1] = k;
                    }
                }
            }
        }

        return dp[n - 1][m - 1];
    }
}

二分

import java.util.Arrays;

class Solution {

    private static boolean trySplit(int[] nums, int m, int target) {
        int count = 1;
        int sum = 0;
        for (int num : nums) {
            sum += num;
            if (sum > target) {
                count++;
                sum = num;
            }
        }
        return count <= m;
    }

    public static int splitArray(int[] nums, int m) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        int left = Arrays.stream(nums).max().getAsInt();
        int right = Arrays.stream(nums).sum();

        int ret = right;

        while (left <= right) {
            int mid = (left + right) >> 1;
            if (trySplit(nums, m, mid)) {
                ret = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }

        return ret;
    }
}

posted @ 2021-12-09 16:53  Tianyiya  阅读(43)  评论(0)    收藏  举报