题目链接:http://115.159.40.116/problem_show.php?pid=5264
题目描述
理工学院的枇杷快熟了,ok,大家都懂得。而且大家都知道,学校的枇杷树都是一列一列的。现在小Y同学已经在筹划怎么摘枇杷了。现在我们假设有一列枇杷树,而且每棵枇杷树上枇杷果的数量小Y都已经知道了。
假设现在有n棵枇杷树,小Y可以把这n棵枇杷树分成m组,每组枇杷果的数量是这组内每棵枇杷树上枇杷果数量的和。注意,每组的枇杷树必须是连续的。(每组最少1棵树,最多n棵树)。小Y把枇杷往寝室拿的时候是一组一组拿的,所花费的力气等于这m组中枇杷果最多的那组枇杷果的数量。现在小Y想花尽量少的力气把这些枇杷果拿回寝室。
输入
每组测试数据第一行有两个数n(n <= 1000)和m(1 <=m <= n)
第二行有n个数,分别代表每颗树上枇杷果的数量
输出
样例输入
3 2 1 2 3 7 5 1 4 3 1 5 2 4
样例输出
3 5
解释:
把一个包含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,效率一下子就上来了。
题目代码:
#include<iostream>
#include<cstdio>
#define M 1000+5
using namespace std;
int a[M],n,m,mmax,sum;
bool is_part(int mid){
int line=0,i,s=0;
for(i=0; i<n; ++i){
//if(a[i]>mid) return false;
if(s+a[i]>mid){
line++;
s=a[i];
if(line>m-1) return false;
}else s+=a[i];
}
return true;
}
int bi_search(){
int l=mmax,r=sum;
while(l<=r){
int mid=(r+l)/2;
if(is_part(mid)) r=mid-1;
else l=mid+1;
}
return l;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
sum=0,mmax=0;
for(int i=0; i<n; ++i){
scanf("%d",&a[i]);
sum+=a[i];
mmax=max(mmax,a[i]);
}
printf("%d\n",bi_search());
}
return 0;
}
资料链接:http://www.lai18.com/content/938413.html
资料链接:http://blog.csdn.net/lyhvoyage/article/details/23263275