长度为N的正整数数列A ,将其分成M小段,每小段连续,要求每小段数字之和的最大值最小。
例如,将数列[4,2,4,5,1]要分成3小段:
若分为[4,2],[4,5],[1],各小段的和分别为6,9,1 ,和的最大值为9;
若分为[4],[2,4],[5,1],各小段的和分别为4,6,6,和的最大值为6;
并且无论如何分段,最大值不会小于6。
所以[4,2,4,5,1]要分成3小段,每小段和的最大值最小为6。
输入格式:
第1行包含两个正整数;
第2行包含N个由空格隔开的非负整数(所有数字之和不超过109),构成数列A 。
输出格式:
仅包含一个正整数,即每小段的和的最大值最小为多少。
输入样例:
5 3
4 2 4 5 1
输出样例:
6
分析:
最大值的最小值:二分的标志
我们的二分是从这个数列中的最大值到数列总和,为什么?
从最大值开始是因为这个序列最少一段的数量是一个数字,还要这一段数字最大,所以要从最大的数开始。到总和结束是因为小于总和的值都有可能是最后的答案,需要二分来判别
代码
#include<iostream>
using namespace std;
int n,m,arr[100000];
int f(int mid) //判断每一段是否都小于mid
{
int i,count=0,sum=0; //count用于计算多少个小段,sum为每小段的和
for(i=0;i<n;i++)
{
if(sum+arr[i]<=mid)
sum+=arr[i];
else
{
count++;
sum=arr[i];
}
}
if(count>=m)
return 1;
else
return 0;
}
int main()
{
ios::sync_with_stdio(false);
int i,lift=0,right=0,mid;
cin>>n>>m;
for(i=0;i<n;i++)
{
cin>>arr[i];
right+=arr[i];
if(lift<arr[i])
lift=arr[i];
}
while(lift<=right)
{
mid=(lift+right)/2;
if(f(mid))
lift=mid+1;
else
right=mid-1;
// cout<<lift<<" "<<right<<endl;
}
cout<<lift;
return 0;
}