Welcome to Subscribe On Youtube

Formatted question description: https://leetcode.ca/all/410.html

410. Split Array Largest Sum (Hard)

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:

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.

Companies:
Google

Related Topics:
Binary Search, Dynamic Programming

Solution 1. DP

Let dp[m][i] be the answer to the subproblem with m subarrays within A[0..i].

Let sum[i][j] be the sum of numbers in A[i..j].

dp[1][i] = sum[0][i]
dp[k][i] = min(max(dp[k-1][j-1], sum[j][i]) | m - 1 <= j <= i)
// OJ: https://leetcode.com/problems/split-array-largest-sum/
// Time: O(N^2 * M)
// Space: O(N^2 + NM)
class Solution {
    typedef long long LL;
public:
    int splitArray(vector<int>& A, int M) {
        int N = A.size();
        vector<vector<LL>> sum(N, vector<LL>(N, 0)), dp(M + 1, vector<LL>(N, INT_MAX));
        for (int i = 0; i < N; ++i) {
            LL s = 0;
            for (int j = i; j < N; ++j) {
                sum[i][j] = (s += A[j]);
            }
        }
        for (int i = 0; i < N; ++i) dp[1][i] = sum[0][i];
        for (int m = 2; m <= M; ++m) {
            for (int i = m - 1; i < N; ++i) {
                for (int j = m - 1; j <= i; ++j) {
                    dp[m][i] = min(dp[m][i], max(dp[m - 1][j - 1], sum[j][i]));
                }
            }
        }
        return dp[M][N - 1];
    }
};

Solution 2. DP

We can compute sum on the fly.

// OJ: https://leetcode.com/problems/split-array-largest-sum/
// Time: O(N^2 * M)
// Space: O(NM)
class Solution {
    typedef long long LL;
public:
    int splitArray(vector<int>& A, int M) {
        int N = A.size();
        vector<vector<LL>> dp(M + 1, vector<LL>(N, INT_MAX));
        LL s = 0;
        for (int i = 0; i < N; ++i) dp[1][i] = (s += A[i]);
        for (int m = 2; m <= M; ++m) {
            for (int i = m - 1; i < N; ++i) {
                LL sum = 0;
                for (int j = i; j >= m - 1; --j) {
                    sum += A[j];
                    dp[m][i] = min(dp[m][i], max(dp[m - 1][j - 1], sum));
                }
            }
        }
        return dp[M][N - 1];
    }
};

Solution 3. DP + Space Optimization

Given the dp dependency, we can just use 1D dp array.

// OJ: https://leetcode.com/problems/split-array-largest-sum/
// Time: O(N^2 * M)
// Space: O(N)
class Solution {
    typedef long long LL;
public:
    int splitArray(vector<int>& A, int M) {
        int N = A.size();
        vector<LL> dp(N, INT_MAX);
        LL s = 0;
        for (int i = 0; i < N; ++i) dp[i] = (s += A[i]);
        for (int m = 2; m <= M; ++m) {
            for (int i = N - 1; i >= m - 1; --i) {
                LL sum = 0;
                dp[i] = INT_MAX;
                for (int j = i; j >= m - 1; --j) {
                    sum += A[j];
                    dp[i] = min(dp[i], max(dp[j - 1], sum));
                }
            }
        }
        return dp[N - 1];
    }
};

Solution 4. Binary Answer

The answer is between the maximum element and the sum of all the elements.

// OJ: https://leetcode.com/problems/split-array-largest-sum/
// Time: O(N * log(S)) where S is sum of nums.
// Space: O(1)
// Ref: https://discuss.leetcode.com/topic/61324/clear-explanation-8ms-binary-search-java
class Solution {
    typedef long long LL;
    int split(vector<int> &A, LL M) {
        LL cnt = 0, sum = 0;
        for (int i = 0; i < A.size(); ++i) {
            sum += A[i];
            if (sum > M) {
                sum = A[i];
                ++cnt;
            }
        }
        return cnt + 1;
    }
public:
    int splitArray(vector<int>& A, int m) {
        LL sum = accumulate(begin(A), end(A), (LL)0);
        if (m == 1) return sum;
        LL L = *max_element(begin(A), end(A)), R = sum;
        while (L <= R) {
            LL M = (L + R) / 2;
            int n = split(A, M);
            if (n <= m) R = M - 1;
            else L = M + 1;
        }
        return L;
    }
};
  • class Solution {
        public int splitArray(int[] nums, int m) {
            int length = nums.length;
            int[][] dp = new int[length + 1][m + 1];
            int[] subSum = new int[length + 1];
            for (int i = 0; i <= length; i++) {
                for (int j = 0; j <= m; j++)
                    dp[i][j] = Integer.MAX_VALUE;
            }
            for (int i = 0; i < length; i++)
                subSum[i + 1] = subSum[i] + nums[i];
            dp[0][0] = 0;
            for (int i = 1; i <= length; i++) {
                for (int j = 1; j <= m; j++) {
                    for (int k = 0; k < i; k++)
                        dp[i][j] = Math.min(dp[i][j], Math.max(dp[k][j - 1], subSum[i] - subSum[k]));
                }
            }
            return dp[length][m];
        }
    }
    
    ############
    
    class Solution {
        public int splitArray(int[] nums, int k) {
            int left = 0, right = 0;
            for (int x : nums) {
                left = Math.max(left, x);
                right += x;
            }
            while (left < right) {
                int mid = (left + right) >> 1;
                if (check(nums, mid, k)) {
                    right = mid;
                } else {
                    left = mid + 1;
                }
            }
            return left;
        }
    
        private boolean check(int[] nums, int mx, int k) {
            int s = 1 << 30, cnt = 0;
            for (int x : nums) {
                s += x;
                if (s > mx) {
                    ++cnt;
                    s = x;
                }
            }
            return cnt <= k;
        }
    }
    
  • // OJ: https://leetcode.com/problems/split-array-largest-sum/
    // Time: O(N * log(S)) where S is sum of nums.
    // Space: O(1)
    // Ref: https://discuss.leetcode.com/topic/61324/clear-explanation-8ms-binary-search-java
    class Solution {
    public:
        int splitArray(vector<int>& A, int m) {
            long sum = accumulate(begin(A), end(A), 0L);
            if (m == 1) return sum;
            long L = *max_element(begin(A), end(A)), R = sum, N = A.size();
            auto valid = [&](int maxSum) {
                int cnt = 1, i = 0 ;
                for (int sum = 0; i < N && cnt <= m; ++i) {
                    sum += A[i];
                    if (sum > maxSum) {
                        sum = A[i];
                        ++cnt;
                    }
                }
                return i == N && cnt <= m;
            };
            while (L <= R) {
                long M = L + (R - L) / 2;
                if (valid(M)) R = M - 1;
                else L = M + 1;
            }
            return L;
        }
    };
    
  • class Solution:
        def splitArray(self, nums: List[int], m: int) -> int:
            def check(x):
                s, cnt = 0, 1
                for num in nums:
                    if s + num > x:
                        cnt += 1
                        s = num
                    else:
                        s += num
                return cnt <= m
    
            left, right = max(nums), sum(nums)
            while left < right:
                mid = (left + right) >> 1
                if check(mid):
                    right = mid
                else:
                    left = mid + 1
            return left
    
    ############
    
    class Solution(object):
      def splitArray(self, nums, m):
        """
        :type nums: List[int]
        :type m: int
        :rtype: int
        """
    
        def valid(nums, target, m):
          count = 1
          total = 0
          for num in nums:
            total += num
            if total > target:
              count += 1
              total = num
              if count > m:
                return False
          return True
    
        start, end = max(nums), sum(nums)
        mid = 0
        while start <= end:
          mid = start + (end - start) / 2
          if valid(nums, mid, m):
            end = mid - 1
          else:
            start = mid + 1
    
        return start
    
    
  • func splitArray(nums []int, k int) int {
    	left, right := 0, 0
    	for _, x := range nums {
    		left = max(left, x)
    		right += x
    	}
    	return left + sort.Search(right-left, func(mx int) bool {
    		mx += left
    		s, cnt := 1<<30, 0
    		for _, x := range nums {
    			s += x
    			if s > mx {
    				s = x
    				cnt++
    			}
    		}
    		return cnt <= k
    	})
    }
    
    func max(a, b int) int {
    	if a > b {
    		return a
    	}
    	return b
    }
    
  • function splitArray(nums: number[], k: number): number {
        let left = 0;
        let right = 0;
        for (const x of nums) {
            left = Math.max(left, x);
            right += x;
        }
        const check = (mx: number) => {
            let s = 1 << 30;
            let cnt = 0;
            for (const x of nums) {
                s += x;
                if (s > mx) {
                    s = x;
                    ++cnt;
                }
            }
            return cnt <= k;
        };
        while (left < right) {
            const mid = (left + right) >> 1;
            if (check(mid)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
    
    

All Problems

All Solutions