之前发了一个转载的二分博客,详见https://blog.csdn.net/yangyanbin_sam/article/details/138767413?spm=1001.2014.3001.5502。
这篇博客来讲一讲我自己的见解。
1.二分简介
二分包括二分查找和二分答案,本质就是分成两半。
二分查找枚举下标,二分答案直接枚举答案。
在数据较小的情况下,直接普通枚举即可,二分适用于数据≥2000000的情况。
2.二分原理
为什么二分效率这么高呢?因为2得n次方很大。
1000000个数据,普通查找需要1000000次。
2的20次方=1048576,所以二分仅需20次左右即可找到。
通俗一点,二分其实就是猜数字游戏,一个一个猜,最多要猜n次;二分就是咔一半:
n=1-10000,anser=4321
从1开始一个一个猜,要猜4321次;从10000开始试,要试5680次;但用二分:
1-5000
2500-5000
3750-5000
3750-4375
4062-4375
4218-4375
4296-4375
4296-4335
4315-4335
4315-4325
4320-4325
4320-4322
4321 猜中,一共猜了13次,比一个一个猜快太多了。
3.二分模版
while(l<r){
int mid = (l+r/2);
if(check(mid)) r = mid;//check= true的时候 往左继续二分
else l = mid +1;
}
while(l<r){
int mid = (l+r+1)/2;
if(check(mid)) l=mid;
else r = mid - 1;
}
二分的关键,在于check()函数,需要根据情况编写。
4.二分答案例题
1.木材加工
【问题描述】
木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头(木头有可能有剩余),需要得到的小段的数目是给定的。当然,我们希望得到的小段越长越好,你的任务是计算能够得到的小段木头的最大长度。木头长度的单位是cm。原木的长度都是正整数,我们要求得到的小段木头的长度也是正整数。
【输入格式】
第一行是两个正整数N和K(1≤N≤100000,1≤K≤200000),N是原木的数目,K是需要得到的小段的数目。接下来的N行,每行有一个1到10000之间的正整数,表示一根原木的长度L。
【输出格式】
输出能够切割得到的小段的最大长度。如果连1cm长的小段都切不出来,输出“0”。
【输入样例】
3 7
50
60
70
【输出样例】
23
这题直接枚举需要n*L次,100000*10000已经超出了for循环范围,会超时,要用二分,代码如下:
#include<bits/stdc++.h>
using namespace std;
long long n,m,l,r=1e5*2*1e5,a[100005];
bool check(long long x)
{
long long d=0;
for(int i=1;i<=n;i++)
{
d+=a[i]/x;
}
return d>=m;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
while(l+1<r)
{
long long mid=l+(r-l)/2;
if(check(mid)) l=mid;
else r=mid;
}
cout<<l;
return 0;
}
枚举小段的长度。
2.Monthly Expense
FJ是一个出色的会计师,他意识到他的农场将要入不敷出。于是他把N(1 ≤ N ≤ 100,000)天以内的每天的开支moneyi(1 ≤ moneyi ≤ 10,000)记下来。FJ想做M (1 ≤ M ≤ N)份的预算,每份预算包括一天或连续多天,求这M份预算的最大值的最小值是多少。
输入格式:
第一行,两个整数N和M
接下来N行,每行一下整数,表示第i天的开支。
输出格式:
一个整数,表示预算的M份预算的最大值的最小值
输入样例:
7 5
100
400
300
100
500
101
400
输出样例
500
样例解释:
第一、 二天做一份预算,第三、四天做一份预算,第5天一份,第6天一份,第7天一份,那么各份预算最大的是500。其他划分方法都不能比这个更少。
通过枚举预算量即可。代码:
#include<bits/stdc++.h>
using namespace std;
long long n,m,l,r,a[100005];
bool check(long long x)
{
long long d=0,s=0;
for(int i=1;i<=n;i++)
{
if(s+a[i]<=x)s+=a[i];
else s=a[i],d++;
}
return d<m;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
l=max(l,a[i]);//最少也要是n份里面的最大值,不然装不下。
r+=a[i];//最多就是所有预算都算一份。
}
while(l<r)
{
long long mid=(l+r)/2;
if(check(mid)) r=mid;//如果组数没超过m,则代表预算可以更小。
else l=mid+1;//否则就是预算太小了。
}
cout<<l;
return 0;
}//本程序是在看过题解后自行写出的(本人已理解)。
你会了吗?
(本文章禁止转载)