给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
注意:
数组长度 n 满足以下条件:
1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
示例:
输入:
nums = [7,2,5,10,8]
m = 2
输出:
18
解释:
一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8],
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。
1. 动态规划
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
int n = nums.size();
vector<long> s(n + 1, 0);
for (int i = 0; i < n; ++i)
s[i + 1] = s[i] + nums[i];
vector<vector<long>> f(n + 1, vector<long>(m + 1, LONG_MAX));
for (int i = 1; i <= n; ++i)
{
f[i][1] = s[i];
for (int j = 2; j <= min(i, m); ++j)
{
for (int k = 1; k <= i - j + 1; ++k)
f[i][j] = min(f[i][j], max(f[i - k][j - 1], s[i] - s[i - k]));
}
}
return f[n][m];
}
};
2. 二分法
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
long low = 0, high = 0, mid;
for (auto x: nums)
{
if (low < x)
low = x;
high += x;
}
while (low < high)
{
mid = (low + high) / 2;
int cnt = 1;
long sum = 0;
for (auto x: nums)
{
sum += x;
if (sum > mid)
{
cnt++;
sum = x;
}
}
if (cnt > m)
low = mid + 1;
else
high = mid;
}
return low;
}
};