【关于二分】

之前发了一个转载的二分博客,详见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份预算的最大值的最小值是多少。 
输入格式: 
第一行,两个整数NM 
接下来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;
}//本程序是在看过题解后自行写出的(本人已理解)。

你会了吗?

(本文章禁止转载)

  • 30
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值