题目:http://ybt.ssoier.cn:8088/problem_show.php?pid=1436
思路:很明显的二分,以mid为假定的和的最大值中的最小值
首先边界需要找一下
大边界为全部数字的和
小边界为数列中最大的那个,因为我们“最大值的最小值”无论如何也不会小于那个大数
然后,假定值mid就找出来了
以这个mid来给数列分段
如果分段少于要求的段数,那就证明数字估大了,需要向里收
否则就往大了算
需要注意的是,在我们得到l和r时还需要一次判断
#include<bits/stdc++.h>
using namespace std;
int n,m,a[100001],l=-1e6,r=0;
int f(int mid){//判断用的函数
int i,sum=0,g=1;//sum为假定值铺路,g是分段用的
for(i=1;i<=n;++i){
sum+=a[i];//
if(sum>mid){//大于了我们假定的最大值
sum=a[i];
g++;//分一段
}
}
if(g<=m){//段数少了,证明数估大了(等于的作用在后面)
return 1;//返回真
}
return 0;//否则返回假
}
int main(){
int i;
scanf("%d%d",&n,&m);//常规操作
for(i=1;i<=n;++i){
scanf("%d",&a[i]);
l=max(l,a[i]);//假定值的最小值
r+=a[i];//假定值的最大值
}
while(r-l>1){
int mid=(l+r)/2;
if(f(mid)){//对假定值进行验证
//返回真,说明估大了
r=mid;//往小了收
}
else{
l=mid;//往大了收
}
}
if(f(l)){
//目前光凭借上面的while,我们无法确定是l还是r
//函数内等于号的作用就在这一步验证上
printf("%d",l);
}
else{
printf("%d",r);
}
return 0;
}
总结:题目给予了一个数列,要求我们分段
分段后一段的和是一个,n段的和就是n个
在这n个中找到一个最大值,这是第二个“一个”
无数种分法代表无数个“一个”,找到最小的“一个”
思路简述就是“假设最小值进行判断“,考二分的动机极其明显
难点在于2边界和结尾的判断