首页 > 代码库 > 410. Split Array Largest Sum

410. Split Array Largest Sum

Given an array which consists of non-negative integers and an integer m, 
you can split the array into m non-empty continuous subarrays.
Write an algorithm to minimize the largest sum among these m subarrays. Note: Given m satisfies the following constraint:
1 ≤ m ≤ length(nums) ≤ 14,000. Examples: Input: nums = [7,2,5,10,8] m = 2 Output: Explanation: There are four ways to split nums into two subarrays. The best way is to split it into [7,2,5] and [10,8], where the largest sum among the two subarrays is only 18.

二分法:

public int splitArray(int[] nums, int m) {
        long sum = 0;
        int max = 0;
        for(int num: nums){
            max = Math.max(max, num);
            sum += num;
        }
        return (int)binarySearch(nums, m, sum, max);
    }
    //二分查找
    private long binarySearch(int[] nums, int m, long high, long low){
        long mid = 0;
        while(low < high){
            mid = (high + low)/2;
            //验证是否满足,也就是这么大的值有可能出现么
            if(valid(nums, m, mid)){
                high = mid;
            }else{
                low = mid + 1;
            }
        }
        return high;
    }

    /**
     * 验证这个值是否合法
     * */
    private boolean valid(int[] nums, int m, long max){
        int cur = 0;
        int count = 1;
        //是否有多余m个片段or区间,大于给定值的max的,如果有了,那么就不合法了,因为这样划分就不止m个,及max太小
        for(int num: nums){
            cur += num;
            if(cur > max){
                cur = num;
                count++;
                if(count > m){
                    return false;
                }
            }
        }
        return true;
}

动归: 最大, 最小, 不能排序, 而且是求区间和的改写, 先考虑用一维动归, dp[m]表示分为m组,  写不出状态转移方程. 并且, 这是最大值最小的问题, 先找最大, 再找最小, 所以用二维数组, 题中就是组数, 和第几个人是变量, 因此dp[m][n] 表示将n个人分为m组的结果值, 这样有了两层外循环, 看递推公式与两层外循环的关系:

dp[m][n] =  min (dp[m][n], Un-1 j = m - 1  max(dp[m - 1][j], sum[n] - sum[j]) (内循环多是最后一次分割点位置变化, 和前一次分割的关系 )

因为dp[m - 1][j] 不能直接得到, 所以需要 外循环 动归到dp[m][n], 其实想想也很简单, 只有外循环先建立起来了, 才有内循环的递推公式. 

一维状态方程常考虑: 要不要该点, 

二维状态方程常考虑: 分割点(与题目的值(sums[j] - sums[k]) 联系最紧密 , 当前分割的次数与前一次数有什么关系

subarray 和的问题常转化为 区间和的问题, (sums[j] - sums[k])

起点的初始化要正常, 中间点的初始化时为了比较(min, max) 等.------知道的初始化正常值, 不知道的初始化为对立值.

 

 

public int splitArray(int[] nums, int m) {
        int n = nums.length;
        int[][] dp = new int[m + 1][nums.length + 1];
     
        int[] sums = new int[n + 1];
        for (int i = 1; i <= n; ++i) {
            sums[i] = sums[i - 1] + nums[i - 1];
            
        }
        for (int i = 0; i <= m; ++i) {
            for (int j = 0; j <= n; ++j) {
                dp[i][j] = Integer.MAX_VALUE;
            }
        }
        dp[0][0] = 0;
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                for (int k = i - 1; k < j; ++k) {
                    int val = Math.max(dp[i - 1][k], sums[j] - sums[k]);
                    dp[i][j] = Math.min(dp[i][j], val);
                }
            }
        }
        return dp[m][n];
}

  

410. Split Array Largest Sum