问题描述:
把一个包含n个正整数的序列划分成m个连续的子序列。设第i个序列的各数之和为S(i),求所有S(i)的最大值最小是多少?
例如序列1 2 3 2 5 4划分为3个子序列的最优方案为 1 2 3 | 2 5 | 4,其中S(1),S(2),S(3)分别为6,7,4,那么最大值为7;
如果划分为 1 2 | 3 2 | 5 4,则最大值为9,不是最小。
问题分析:
能否使m个连续子序列所有的s(i)均不超过x,则该命题成立的最小的x即为答案。该命题不难判断,只需贪心,每次尽量从左
向右尽量多划分元素即可。
我们把该问题转化为递归分治问题,类似于二分查找。首先取Sum和元素最大值的中值x,如果命题为假,那么答案比x大;
如果命题为真,则答案小于等于x。问题得解,复杂度为O(n*logSum)
代码:
#include<iostream> #include<vector> using namespace std; int divide = 3; int main() { vector<int> a = { 9, 19, 15, 13, 13, 9, 14, 1, 1, 7 }; int max = INT_MIN; int sum = 0; for (int i = 0; i < a.size(); i++) { if (max < a[i]) { max = a[i]; } sum += a[i]; } int left = max; int right = sum; int min = INT_MAX; while (left < right) { int middle = (left + right) / 2; int m = 0; int n = 0; int i; for (i = 0; i < a.size(); i++) { m += a[i]; if (m<=middle) continue; m = a[i]; n++; } n++; if (n <= divide) { if (middle < min) min = middle; right = middle - 1; } else left = middle + 1; } cout << min << endl; }