红桃J

用心写好每行完美的代码,远比写一堆更有价值

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

问题描述:

把一个包含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;
}

 

posted on 2015-05-06 23:23  红桃J  阅读(1568)  评论(0编辑  收藏  举报