410. Split Array Largest Sum

Description

<nav class="tab-view hide-scrollbar" style="box-sizing: border-box; display: block; overflow-y: hidden; overflow-x: auto; white-space: nowrap; margin-top: 32px;">Solution</nav>

Pick One


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:
If n is the length of array, assume the following constraints are satisfied:

  • 1 ≤ n ≤ 1000
  • 1 ≤ m ≤ min(50, n)

Examples:

<pre style="box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13px; display: block; padding: 9.5px; margin: 0px 0px 10px; line-height: 1.42857; color: rgb(51, 51, 51); word-break: break-all; word-wrap: break-word; background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 4px;">Input:
nums = [7,2,5,10,8]
m = 2

Output:
18

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.</pre>

Solution

DFS, time O(n ^ m) (TLE), space O(n)

直观的解法,但会超时。

class Solution {
    private int maxSum;
    
    public int splitArray(int[] nums, int m) {
        maxSum = Integer.MAX_VALUE;
        dfs(nums, 0, Integer.MIN_VALUE, m);
        return maxSum;
    }
    
    public void dfs(int[] nums, int start, int max, int m) {
        if (start == nums.length) {
            if (m == 0) {
                maxSum = Math.min(maxSum, max);
            }
            return;
        }
        
        if (nums.length - start < m) {
            return;
        }
        
        int sum = 0;
        for (int i = start; i < nums.length; ++i) {
            sum += nums[i];
            max = Math.max(max, sum);
            dfs(nums, i + 1, max, m - 1);
        }
    }
}

DP, time O(n ^ 2 * m), space O(n * m)

这道题让我想起了算法导论上的一道切绳子?的题。可以用二维dp去做,dp[i][j]表示将nums中前i个元素split成j段的maxSum。

注意dp[][]的初始化!

class Solution {
    public int splitArray(int[] nums, int m) {
        int n = nums.length;
        int[][] dp = new int[n + 1][m + 1];
        for (int [] row : dp) {
            Arrays.fill(row, Integer.MAX_VALUE);    // important
        }
        
        dp[0][0] = 0;
        
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= Math.min(i, m); ++j) {
                int sum = 0;
                for (int k = i; k >= j; --k) {
                    sum += nums[k - 1];
                    dp[i][j] = Math.min(Math.max(sum, dp[k - 1][j - 1]), dp[i][j]);
                }
            }
        }
        
        return dp[n][m];
    }
}

Binary search + Greedy, time O(n∗log(sum of array)), space O(1)

我还能说什么呢,这个解法是真牛逼。。

The answer is between maximum value of input array numbers and sum of those numbers.
Use binary search to approach the correct answer. We have l = max number of array; r = sum of all numbers in the array;Every time we do mid = (l + r) / 2;
Use greedy to narrow down left and right boundaries in binary search.

  1. Cut the array from left.
  2. Try our best to make sure that the sum of numbers between each two cuts (inclusive) is large enough but still less than mid.
  3. We'll end up with two results: either we can divide the array into more than m subarrays or we cannot.
  • If we can, it means that the mid value we pick is too small because we've already tried our best to make sure each part holds
    as many non-negative numbers as we can but we still have numbers left.
    So, it is impossible to cut the array into m parts and make sure each parts is no larger than mid. We should increase m.
    This leads to mid = l + 1;
  • If we can't, it is either we successfully divide the array into m parts and the sum of each part is less than mid,
    or we used up all numbers before we reach m. Both of them mean that we should lower mid because we need to find the minimum one. This leads to r = mid - 1;
class Solution {
    public int splitArray(int[] nums, int m) {
        long sum = 0;   // use long to avoid overflow
        int max = 0;
        for (int num : nums) {
            sum += num;
            max = Math.max(num, max);
        }
        
        long left = max;
        long right = sum;
        
        while (left <= right) {
            long mid = left + (right - left) / 2;
            
            if (!isValid(nums, m, mid)) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        
        return (int) left;
    }
    
    public boolean isValid(int[] nums, int m, long target) {
        long sum = 0;
        int count = 1;
        
        for (int num : nums) {
            sum += num;
            if (sum <= target) {
                continue;
            }
            
            sum = num;
            if (++count > m) {
                return false;
            }
        }
        
        return true;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,449评论 0 10
  • 更活泼开朗,更天真灵动,更澄澈单纯,才是向往的模样,不是吗。 松浦弥太郎在第一章第三节的部分写“给需要别人肯定的你...
    不着子阅读 139评论 0 0
  • “你们办公室方位不对,可能会有点不顺。” “看到对面楼立起来的架子吗?正对着你们幼儿园主位,财运被压制...
    梅森刘阅读 648评论 0 0
  • 鸡汤火锅 烤羊腿 红焖虾 粉蒸排骨 蚝汁鲍鱼 香煎鳊鱼 泥蒿炒鸭肫 彩椒牛肉 香辣花蛤 清炒时蔬 豌豆炒腊肠 八宝饭
    安_9fd3阅读 331评论 0 0