您的位置:首页 > 理论基础 > 数据结构算法

【算法设计与数据结构】二分法解决最大值最小化问题——入门篇

2015-09-03 13:54 387 查看

问题描述

把一个包含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,不是最小。

解题思路

我们对问题做一些转化:

在一次划分中,求一个x,使得x满足:对任意的S(i),都有S(i)<=x;这个条件保证了x是所有S(i)中的最大值。我们需要求的就是满足该条件的最小的x。

有了这个思路之后,我们继续分析如何找到这个x,首先,可以知道的是,max <= x <= sum。

接下来先是最朴素的想法:枚举每一个x,贪心地每次从左向右尽量多划分元素,但是S(i)不能超过x,而且划分的子序列个数不能超过m个(即所用划分线不能超过m-1条)

以上方法当然可行,但是每个x都遍历一次太浪费时间了。

问题经过转化,现在变成了在[max, sum]中间查找一个满足条件的x,查找的问题,相信大家对二分搜索并不陌生。这个时候,用二分搜索的思想来求x,效率一下子就上来了。

代码

int max = 元素中的最大值;
int sum = 所有元素之和; 

//是否能把序列划分为每个序列之和不大于x的m个子序列
bool is_part(int x) 
{
    //每次往右划分,划分完后,所用的划分线不大于m-1个即可
    int line = 0, s = 0;
    for(int i = 0; i < n; i++)
    {
        if(s+A[i] > x)  //和大于x,不能再把当前元素加上了 
        {
            line++;     //加一条分隔线
            s = A[i];
            if(line > m-1)  //分隔线已经超过m-1条 
                return false; 
        }
        else
        {
            s+=A[i];    //把当前元素与前面的元素连上,以便尽量往右划分,贪心到底 
        }
    }
    return true;
}

int binary_solve()
{
    int l = max, r = sum; 
    while (l < r)
    {
        int m = l + (r-l)/2;
        if (is_part(m)) r = m;
        else l = m + 1;
    }
    return x;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: