浅聊二分答案--《算法竞赛进阶指南》学习

首先先讲一些小的知识点:

(1)向零取整: 举例:1.5取整,为1而不是2,因为1离0更近

(2)所谓向上取整指当计算的结果不为整数时取大于计算结果的整数,向下取整指当计算的结果不为整数时取小于计算结果的整数.当计算结果为整数时直接取整数。
举例:4.9,向下取整为4,向上取整为5.
这所以讲这个知识点呢,是因为想搞清楚"/2"和“>>1”的不同点,前者就是向零取整,后者就是向下取整啦~~

说实话,对**二分答案**的理解,我觉得别人的文章会比我有见解的多,所以就直接转给大家啦啦啦啦
[二分答案算法超详细教程](https://blog.csdn.net/weixin_44179892/article/details/104197011)大家可以点击看看哦~~(不过别忘了回来我这不然好想哭)*

下面我们就一起看看经典的例题吧!

  • 有N本书排成一行,已知第i本的厚度是Ai。把它们分成连续的M组,使T最小化。T表示厚度之和最大的一组的厚度。输出T。
#include <bits/stdc++.h>
 using namespace std;
 int n,m,a[10000],sum_of_ai;
 bool valid(int size)
 {
 	int group=1,rest=size;
 	for(int i=1;i<=n;i++)
 	{
 		if(rest>=a[i])//mid在a[i]右边 
 		rest-=a[i];
 		else{
 			group++;
 			rest=size-a[i];
		 }
	 }
	 return group<=m;
 }
 int main()
 {
 	scanf("%d %d",&n,&m);//n本书,m组 
 	for(int i=1;i<=n;i++)
 	{
 		scanf("%d",&a[i]);
 		sum_of_ai+=a[i];
	}
 	int l=0,r=sum_of_ai;//sum_of_ai代表书的厚度总和 
 	while(l<r)
 	{
 		int mid=(l+r)/2;
 		if(valid(mid)) r=mid;
 		else l=mid+1;
	}
	 cout<<l<<endl;
 }

这道题的思路我真的想了好久滴呐~~~~久到我怀疑人森
大家可以自己用个例子试试看,感受一下,这里我就讲讲我的想法啦~

  • 清楚我们用的是二分答案的方式。这里“答案”要重点理解,可以在接下来我举的例子中好好feel一下哦

  • 比如我给的一组数是1 2 3 4 5 ,n=5,m=3。那么sum_of_ai=15,看main函数中while语句,第一次中,mid=7,然后就跳到valid函数,经过分析,可得到,group<=m是真的,也就是return 1,所以,回到main函数以后也就是r=mid=7。这里的“真的”要品一下,可以把group++理解为你分了一次组,你分组的次数少于等于m组,也就是你满足了题目中的分组条件,但是呢,你每一组书的厚度之和的最大值就应该大于了T(因为此时l<r也就是还没到分界点)

  • 接着第二次while语句,mid=3(此时r=7,l=0),再来valid函数,经过分析,可以知道这一次return 0,l=4,这里返回的是“假的”值也就是0,可以理解为你分组分多了,所以你每组书的厚度之和的最大值应该能小于T(l<r没到分界点)

  • 再来while,mid=5(此时r=7,l=4),再来valid函数,分析后,这一次return 0,l=6。

  • 再来while,mid=6(此时r=7,l=6),再来valid函数,分析后,return 1,r=6。

  • 此时l与r相等跳出来while,输出l=6.

上图瞅瞅!

就是我举例的二分答案过程
如果有不对的地方还希望大家多多指教!!!蟹蟹蟹蟹啦~

---------------------------------我是分割线哇哎呀不会弄中间-------------------------------------------------

第一次发文实在不懂这个编辑功能咳咳咳……

看看第二题:

  • 给定正整数数列A,求一个平均数最大的,长度不小于L的(连续的)子段

这道题也就是oj一道题的直白表述。
题目截图

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int N,L;
    double eps=1e-5; 
    double a[100001]={0},sum[100001]={0},b[100001]={0};
    double l=2001,r=0;//这里l用2001是因为a[i]最大也就是2000 
    cin>>N>>L;
    for(int i=1;i<=N;i++)
    {
        scanf("%lf",&a[i]);
        r = max(r,a[i]);//r是a[i]里的最大值 
        l = min(l,a[i]);//l是a[i]里的最小值 
    }
    while(r-l>eps)//分界线的感觉哈 
    {
        double mid=(l+r)/2;//二分答案的赶脚哈 
        double ans=-1e9, minn=1e9;
        for(int i=1;i<=N;i++)
            b[i]=a[i]-mid;//记录差值 
        for(int i=1;i<=N;i++)
            sum[i]=sum[i-1]+b[i];//前缀和的赶脚哈 
        for(int i=L;i<=N;i++)
        {
            minn=min(minn,sum[i-L]);//从规定的L开始搜寻min 
            ans=max(ans,sum[i]-minn);
        }
        if(ans>=0) l=mid;
        else   r=mid;
    }
    cout<<int(r*1000)<<endl;
}

这道题的难点我觉得应该是在理解ans范围上,不过我个人感觉这里的理解比上道题简单,这里就不多说啦啦啦啦啦~

各位志同道合的盆友们明天见哦~

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值