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);
}
用处
一般来说,暴力枚举会超时。这时,如果我们用上二分,效率会大大提升。
二分的时间复杂度(一般来说):
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.总结
今天我们讲了整数二分,讲了如何用二分做题与优化。
但这也仅仅是整数二分。
为什么要强调整数,那就肯定也有实数二分啦!
实数二分与整数二分的思路基本一样,但往往要注意的就是浮点数的精度。
所以才分整数与实数二分。
那么下次我们就来讲一下实数二分。