LC410. Split Array Largest Sum
把一个数组分成m个连续子数组(不能有空数组),求所有分法中,子数组sum的最大值的最小值。
方法1:容易想到的是动态规划
dp[i][j] = min(max(dp[k-1][j-1], sum[k][i]) 1 <= k <= i, dp[i][j]表示用前i个数字,分成j组,最大和的最小值
time:$O(nnm)$ space:$O(nm)$
class Solution { public: typedef long long ll; ll INF = 1e15; int splitArray(vector<int>& nums, int m) { int n = nums.size(); vector<vector<ll>> dp(n + 1, vector<ll>(m + 1, INF)); dp[0][0] = 0; vector<ll> sums(n + 1, 0); for (int i = 0; i < n; ++i) sums[i + 1] = sums[i] + nums[i]; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { for (int k = 1; k <= i; ++k) { dp[i][j] = min(dp[i][j], max(dp[k - 1][j - 1], sums[i] - sums[k - 1])); } } } return dp[n][m]; } };
方法2:贪心+二分
check(LL sum, vector<int>& nu) 函数判断数组能否被分成m
段,最大的和不超过sum
,用了贪心的思路,cnt
表示最少能分多少段,使最大和不超过sum
,因此,分当前段的时候,取尽可能多的数,如果最后cnt <= m
,说明我肯定能分出m
段,如果cnt < m,只要拆开一些段就行,最大和并不会变坏。假如某个数大于sum
,则无论怎么分,都无法分出m(m > 1)
段。
然后用二分去找最小的sum
,能使check
返回true
,相当于找lower_bound
。
class Solution { public: typedef long long LL; int m; bool check(LL sum, vector<int>& nu) { LL nsum = 0; int cnt = 1; for (int i : nu) { if (i > sum) return false; if (i + nsum > sum) { cnt++; nsum = i; } else nsum += i; } return cnt <= m; } int splitArray(vector<int>& nums, int m) { int n = nums.size(); this->m = m; LL l = 0, r = 0; for (int i : nums) r += i; LL mid, ans = r; while (l <= r) { mid = (l + r) >> 1; if (check(mid, nums)) { ans = min(ans, mid); r = mid - 1; } else l = mid + 1; } return ans; } };