88888888y

导航

 

题目: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边界和结尾的判断

 

posted on 2022-03-05 10:56  88888888y  阅读(33)  评论(0)    收藏  举报