Leetcode 410. Split Array Largest Sum

Problem:

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.

Solution:

  看到极小化极大值的问题,我的第一个想法就是动态规划,一般来说,这类极小化极大值或极大化极小值问题大概率都是用动态规划来做,而且还有一个特性,就是动态规划一般会把一维问题上升到三维的时间复杂度,比如Leetcode 375。照这个思路往下走,如何进行规划呢。首先是维护一个二维数组,dp[i][j]表示在第j个数之前把子数组分为i份的最小的最大值(有点绕,意思是第j个数之前可能有多种分法,每种分法都有一个和最大的子数组,dp[i][j]即所有分法中最小的最大值),因此,如果将第j个数之前把它分为i+1份,则从i到j进行循环(因为第i个数之前不可能分为i份),计算dp[i][t]和sum[j]-sum[t]中的较大值,在这i到j之间寻找这个较大值的最小值赋给dp[i+1][j],代码如下

 1 class Solution {
 2 public:
 3     int splitArray(vector<int>& nums, int m) {
 4         int value = INT_MAX;
 5         int summary = 0;
 6         vector<int> sum(nums.size(),0);
 7         vector<int> dp(nums.size(),INT_MAX);
 8         for(int i = 0;i != nums.size();++i){
 9             summary += nums[i];
10             sum[i] = summary;
11         }
12         for(int i = 0;i != nums.size();++i)
13             dp[i] = sum[i];
14         for(int i = 1;i != m;++i){
15             for(int j = nums.size()-1;j >= i;--j){
16                 value = INT_MAX;
17                 for(int t = i-1;t != j;++t){
18                     value = min(value,max(dp[t],sum[j]-sum[t]));
19                 }
20                 dp[j] = value;
21             }
22         }
23         return dp.back();
24     }
25 };

  然而这道题还没有结束,通过提交效率的直方图我发现有两个距离差的较远的高峰,而且标签还有一个诡异的Binary Search,这说明这道题还有一个二分搜索的解法。首先我们可以断定答案落在区间[max(nums),sum(nums)]之间,所以我们在这个区间内做二分搜索,并判断当结果为pivot时至少可以分为多少部分,找到最小可以满足分为m份的值。

Code:

 1 class Solution {
 2 public:
 3     int splitArray(vector<int>& nums, int m) {
 4         int left = 0;
 5         int right = 0;
 6         for(int i = 0;i != nums.size();++i){
 7             left = max(left,nums[i]);
 8             right += nums[i];
 9         }
10         while(left < right){
11             int pivot = left+(right-left)/2;
12             int count = 1;
13             int sum = 0;
14             for(int i = 0;i != nums.size();++i){
15                 sum += nums[i];
16                 if(sum > pivot){
17                     sum = nums[i];
18                     count++;
19                 }
20             }
21             if(count <= m)
22                 right = pivot;
23             else
24                 left = pivot+1;
25         }
26         return left;
27     }
28 };

猜你喜欢

转载自www.cnblogs.com/haoweizh/p/10201421.html