1. 程式人生 > >Leetcode 410. Split Array Largest Sum

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 };