题意
给定一个数组,将数组划分m组,要求每组的和的最大值最小
思路
算法1:dp
首先我们这样考虑:我们要将前n个元素划分成m段,即先找一个划分点k,在[k + 1, n]不再划分。然后将[1, k]划分成m - 1段。那么就可以得到我们的状态表示和转移方程。
状态表示:d[i,j],前i个元素,划分成j段的最大和。
转移方程:d[i,j]=min{max0≤k<i{d[k,j−1]},∑p=k+1jap}
时间复杂度:O(n2m)
算法2:二分
最大值最小问题一般采用二分答案的方法。
我们二分一下我们最后的答案,判断答案是否合法即可。
判断数x是否合法:统计一下将数组划分为最大值≤x时能划分多少组。如果组数cnt>x,则说明我们答案应该更大,否则,答案可以减小。
代码
//algorithm 1: dp
const int maxn = 1005;
const int maxm = 55;
int d[maxn][maxm];
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
int n = nums.size();
if (n == 0) return 0;
int S[maxn]; S[0] = nums[0];
for (int i = 1; i < n; i++) S[i] = S[i - 1] + nums[i];
for (int i = 0; i < n; i++) d[i][1] = S[i];
for (int j = 2; j <= m; j++) {
for (int i = 0; i < n; i++) {
d[i][j] = INT_MAX;
for (int k = 0; k < i; k++)
d[i][j] = min(d[i][j], max(d[k][j - 1], S[i] - S[k]));
}
}
return d[n - 1][m];
}
};
//algorithm 2: Binary Search
class Solution {
public:
bool judge(long long x, int m, vector<int> nums) {
int cnt = 0;
bool f = false;
long long sum = 0;
for (int i = 0; i < nums.size(); i++) {
if ((long long)nums[i] > x) return false;
sum += nums[i];
if (i == nums.size() - 1) {
if (sum > x) cnt += 2;
else cnt++;
} else {
if (sum > x) {
cnt++;
sum = nums[i];
}
}
}
if (cnt > m) return false;
return true;
}
int splitArray(vector<int>& nums, int m) {
int n = nums.size();
if (n == 0) return 0;
long long sum = nums[0], MIN = nums[0];
for (int i = 1; i < n; i++) {sum += nums[i]; MIN = min(MIN, (long long)nums[i]);}
long long L = MIN, R = sum, M = L + (R - L) / 2, res = sum;
while (L < R) {
if (R == L + 1) {
if (judge(L, m, nums)) M = L;
else M = R;
break;
}
M = L + (R - L) / 2;
if (judge(M, m, nums)) R = M;
else L = M;
}
return M;
}
};