【C++算法讲解】整数二分

1.算法思想

背景

你和你的朋友玩猜数字游戏,你要在1~100里面选一个数,如果你选错了,你的朋友就会告诉你你猜大了还是猜小了。问题是:你至少要猜几次才能猜中

很明显,你第一次应该猜1到100的中间的数,这样,如果你的朋友告诉你你猜小了,数的范围就缩小到了(50,100],重复这样的操作,最后你肯定能在最小的次数内猜到这个数字。

这就是二分的基本思想。

算法讲解

单这样讲你可能还一头雾水。不过没事,我写个代码你就明白了。

我们来模拟上面的猜数字游戏,不过不是你猜,这次让电脑来猜。

首先得生成一个[1,100]的随机数。

srand(time(0));
int num=rand()%100+1;

然后就开始猜数字。

现在数的范围是[1,100],那么我们就用l和r来表示这个范围。

然后从中间数开始猜,我们就定一个mid变量。

int l=1,r=100;
int mid=(l+r)/2;

猜到什么时候呢?当我们的l和r相等时,也就说明数的范围缩小到了一个数,我们就停止了。

int l=1,r=100,mid;
while(l<=r){
    mid=(l+r)/2;
}

如果猜小了,我们就把范围缩小到[l,mid);相同的,如果猜大了,就把范围缩小到(mid,r]

int l=1,r=100,mid;
while(l<=r){
    mid=(l+r)/2;
    if(mid>num) r=mid-1;
    else if(mid<num) l=mid+1;
}

这时,我们的猜数字游戏就做好了。这就是二分的模版。

#include<bits/stdc++.h>
using namespace std;
int main(){
	srand(time(0));
	int num=rand()%100+1;
	int l=1,r=100,mid;
	while(l<=r){
	    mid=(l+r)/2;
	    if(mid>num) r=mid-1;
	    else if(mid<num) l=mid+1;
	}
	printf("%d\n",mid);
}

用处

一般来说,暴力枚举会超时。这时,如果我们用上二分,效率会大大提升。

二分的时间复杂度(一般来说):\Theta (log_{2}n)

2.例题

了解了什么是二分以后,我们就应该来做一些题。

题目:木材加工


题目描述

   木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头(木头有可能有剩余),需要得到的小段的数目是给定的。当然,我们希望得到的小段越长越好,你的任务是计算能够得到的小段木头的最大长度。木头长度的单位是cm。原木的长度都是正整数,我们要求得到的小段木头的长度也是正整数。

输入输出格式

输入格式:

第一行是两个正整数N和K (1≤N≤100000, 1≤K≤200000), N是原木的数目,K是需要得到的小段的数目。
接下来的N行,每行有一个1到10000之间的正整数,表示一根原木的长度L。

输出格式:

输出能够切割得到的小段的最大长度。如果连1cm长的小段都切不出来,输出"0"。

输入输出样例

输入样例#1:

3 7
232
124
456

输出样例#1:

114

题解

这道题用枚举完全可以做:枚举1到max(a[i]),如果长度合法,则记录下最大值。但考虑到数据范围是1≤N≤100000, 1≤K≤200000,用枚举明显会超时。这时候,我们就可以利用二分来优化了。

我们可以设l=1,r=max(a[i])(a数组记录木材长度),二分求解,一边二分一边记录最大值。

while(l<=r){
    mid=(l+r)/2;
    if(solve(mid)>=k) ans=mid,l=mid+1;
    else r=mid-1;
}

其中solve函数返回能切成多少段长度为mid的木材。

最后输出ans就好了

奉上代码:

#include<bits/stdc++.h>
using namespace std;
int n,k,a[100010],_m=0,ans;
int solve(int o){
	int ans=0;
	for(int i=1;i<=n;i++) ans+=a[i]/o;
	return ans;
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),_m=max(_m,a[i]);
	int l=1,r=_m,mid;
	while(l<=r){
		mid=(l+r)/2;
		if(solve(mid)>=k) ans=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d\n",ans);
}

3.总结

今天我们讲了整数二分,讲了如何用二分做题与优化。

但这也仅仅是整数二分。

为什么要强调整数,那就肯定也有实数二分啦!

实数二分与整数二分的思路基本一样,但往往要注意的就是浮点数的精度。

所以才分整数与实数二分。

那么下次我们就来讲一下实数二分。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值