分治算法——二分算法

  在我们做编程题的过程中,常常会遇到一些题目数据范围过大,需要采用高效率的算法来解决。

  二分算法就是一个O(log2 n)级别的算法

  今天学到的模板我更愿意称之为备胎算法,因为这类题目都有一个共同点

  1、答案应在一个区间、一个范围内,并且这个区间具有单调性

  2、答案是满足条件的最小值或者最大值

  3、有一个“备胎”用来存储满足条件的最佳值

  只要满足这两个条件,我们就可以所以二分算法

  我们先来看一下二分算法的基本应用,二分查找

这是最基础的利用二分算法进行查找的代码

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
int main()
{
    int n,i;
    scanf("%d",&n);
    int a[n+1],l=1,r=n,mid,key,best=0;
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    scanf("%d",&key);
    while(l<=r){          //l==r 时也要进行比较
        mid=(l+r)/2;
        if(a[mid]==key){
            best=mid;
            break;
        }
        if(a[i]>key){   //答案应该在mid的左边
            r=mid-1;
        }else{         //答案应该在mid的右边
            l=mid+1;
        }
    }
    printf("%d\n",best);
    return 0;
}

  接下来我们来看几道二分算法的题目熟悉一下

18935 贪吃的小Q

时间限制:1000MS  代码长度限制:10KB
提交次数:0 通过次数:0

题型: 编程题   语言: G++;GCC

Description

腾讯2018春招技术类编程题

小Q的父母要出差N天,走之前给小Q留下了M块巧克力。
小Q决定每天吃的巧克力数量不少于前一天吃的一半,但是他又不想在父母回来之前的某一天没有巧克力吃,
请问他第一天最多能吃多少块巧克力?

输入格式

一行包含两个正整数,表示父母出差的天数N(N<=50000)和巧克力的数量M(N<=M<=100000)

输出格式

输出一个数表示小Q第一天最多能吃多少块巧克力。

输入样例

3 7

输出样例

4
#include <stdio.h>
#include <stdlib.h>
int main()
{
    int N,M;
    scanf("%d%d",&N,&M);
    int l=1,r=M,mid,best=1,i,sum,day;
    while(l<=r){        
        mid=(l+r)/2;     //现在查找到mid值了
        sum=M;           //判断mid值是否满足条件
        day=mid;
        for(i=1;i<=N;i++){
            sum=sum-day;
            day=(day+1)/2;    
        }
        if(sum>=0){        //如果mid值满足条件,那就是目前的最优值,用best保存这个最优值
            best=mid,l=mid+1;    //更改查找区间,看是否有更优值
        }else{
        r=mid-1;
        }
    }
    printf("%d\n",best);
    return 0;
}

  分析:题目的答案是:第一天最多可以吃多少块巧克力,这意味着我们需要在[1,2,3,4,.........M-2,M-1,M]这个区间里面去找答案,我们第一天可以吃一块或者两块或者.... 最多M块全吃了;这个时候备胎best先初始化为1,因为第一天吃1块是最少的了,如果我们在后面的查找中找到满足条件的mid,那么mid肯定比best大,再用best保存目前的最优值就行了。

best保存最优值,这也是备胎名字的由来! 如果在接下来的查找中,有mid是满足条件的,那best就会被这个更优值给替代掉

mid查找完之后,更改查找区间,寻找是否有满足条件的更优值

9023 砍树

时间限制:1000MS  代码长度限制:10KB
提交次数:0 通过次数:0

题型: 编程题   语言: G++;GCC

Description

洛谷1873
伐木工人米尔科需要砍倒M米长的木材。这是一个对米尔科来说很容易的工作,因为他有一个漂亮的新伐木机,可以像野火一样砍倒森林。
不过,米尔科只被允许砍倒单行树木。
米尔科的伐木机工作过程如下:米尔科设置一个高度参数H(米),伐木机升起一个巨大的锯片到高度H,
并锯掉所有的树比H高的部分(当然,树木不高于H米的部分保持不变)。米尔科就行到树木被锯下的部分。
例如,如果一行树的高度分别为20,15,10和17,米尔科把锯片升到15米的高度,
切割后树木剩下的高度将是15,15,10和15,而米尔科将从第1棵树得到5米,从第4棵树得到2米,共得到7米木材。
米尔科非常关注生态保护,所以他不会砍掉过多的木材。这正是他为什么尽可能高地设定伐木机锯片的原因。
帮助米尔科找到伐木机锯片的最大的整数高度H,使得他能得到木材至少为M米。换句话说,如果再升高1米,则他将得不到M米木材。

输入格式

第1行:2个整数N和M,N表示树木的数量(1<=N<=1000000),M表示需要的木材总长度(1<=M<=2000000000)
第2行:N个整数表示每棵树的高度,值均不超过1000000000。所有木材长度之和大于M,因此必有解。

输出格式

第1行:1个整数,表示砍树的最高高度。

输入样例

5 20
4 42 40 26 46

输出样例

36
#include <stdio.h>
#include <stdlib.h>
int main()
{
    int N,M;
    scanf("%d%d",&N,&M);
    int a[N+1],i;
    for(i=1;i<=N;i++){
        scanf("%d",&a[i]);
    }
    int l=1,r=1000000000,mid,best=0,sum;//初始化查找区间
    while(l<=r){
            mid=(l+r)/2;     //查找到mid值
            sum=0;           //判断mid值是否满足条件
            for(i=1;i<=N;i++){
                if(a[i]>mid){
                    sum=sum+a[i]-mid;
                }
            }

            if(sum>=M){          //mid值满足条件的话,用best保存最有优值
                best=mid,l=mid+1;  //更改查找区间,寻找是否有更优值
            }else{
                r=mid-1;
            }
    }
    printf("%d\n",best);
    return 0;
}

19050 牛牛打气球

时间限制:1000MS  代码长度限制:10KB
提交次数:0 通过次数:0

题型: 编程题   语言: 不限定

Description

贝壳找房2021届校招算法卷3 
有n个气球,每个气球都有一个坚韧度,牛牛有一把全屏武器,可以使每一个气球的坚韧度都下降b(坚韧度不会为负数),特别的:每次释放武器的时候,牛牛可以选择一个气球,使得这个气球多承受a点伤害。
牛牛想知道,最少释放几次武器,可以使得所有气球的坚韧度都变成0呢?

输入格式

第一行三个整数n,a,b。
第二行n个空格隔开的整数,第个数表示第i个气球的坚韧度。
n<=500000,其余所有整数都在[1,10^9]范围内。

输出格式

一个整数表示答案。

输入样例

3 1 2
1 4 5

输出样例

2

提示

第一次释放选择对第三个气球多承受1点伤害,三个气球的坚韧度变成:0 2 2 。第二次释放后所有气球的坚韧度都为0。
注意观察数据范围,这类问题在进行计算时,很容易会超过int范围,所以尽量采用long long类型来存储和计算。
#include<stdio.h>
#include<stdlib.h>
int main()
{
    long int n,a,b;
    scanf("%ld%ld%ld",&n,&a,&b);
    long int c[n+1],d[n+1],i;
    for(i=1;i<=n;i++){
        scanf("%ld",&c[i]);
    }
    long int l=1,r=1e9,mid,best=1e9,sum,cnt;  //初始化查找区间
    while(l<=r){
        mid=(l+r)/2;     //查找到mid值
        sum=mid;          //判断mid是否满足条件
        cnt=0;
        for(i=1;i<=n;i++){
            d[i]=c[i]-b*mid;
            while(d[i]>0&&sum>0){
                d[i]=d[i]-a;
                sum--;
            }
            if(d[i]>0){
                cnt=cnt+d[i];
            }
        }
        if(cnt==0){           //mid满足条件,用best保存最优值
            best=mid,r=mid-1;  //更改查找区间,寻找是否有更优值
        }else{
            l=mid+1;
        }
    }
    printf("%ld\n",best);
    return 0;
}

18725 宇宙迁跃

时间限制:1000MS  代码长度限制:10KB
提交次数:0 通过次数:0

题型: 编程题   语言: G++;GCC

Description

在基地的科学家发明“透镜”之后,宇宙航行变得更加效率。
作为基地元首的的代理人,你需要在K天内乘坐飞船到达首都川陀。
飞船可以花费一天时间,通过迁跃从一个星系到达另一个星系,但绝不能迁跃到星系之间,那样不但会遇到一些自然危险,也可能永远迷失。
我们把基地至川陀间星系的坐标看成是一个线性序列,例如a星系坐标是10,b星系坐标是15,那么飞船必须具备不小于5的迁跃能力才能从a航行至b。
基地坐标为0,请你根据基地至川陀间的N个星系坐标,计算飞船的迁跃能力至少为多大,才能在K天内(包含K天)到达川陀。

输入格式

第一行两个整数N和K。(1=<N<=10000,1=<K<=10000)
第二行N个整数,表示N个星系的坐标ai,题目确保坐标由小到大排列。(0=<ai<=100000)

输出格式

仅一行,飞船的最小迁跃能力。

输入样例

5 2
1 4 6 10 19

输出样例

10

提示

样例说明:川陀的坐标为最后一个值19。
飞船的迁跃能力至少为10,才能在2天内到达川陀。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>

int main()
{
    int N,K;
    scanf("%d%d",&N,&K);
    int a[N+2],i;
    for(i=1;i<=N;i++){
        scanf("%d",&a[i]);
    }
    a[0]=0,a[N+1]=1000000;
    int l=1,r=100000,mid,best=a[N],sum,day;
    while(l<=r){
        mid=(l+r)/2;      //查找到mid值
         sum=0;           //判断mid值是否满足条件
         day=0;
         for(i=1;i<=N;i++){
             if(mid>=(a[i]-sum)&&mid<(a[i+1]-sum)){
                 sum=a[i];
                 day++;
             }
         }
         if(sum==a[N]&&day<=K){   //mid满足条件,best保存目前最优值
             best=mid,r=mid-1;    //更改查找区间,寻找是否有更优值
         }else{
             l=mid+1;
         }
    }
    printf("%d\n",best);
    return 0;
}

  可以看到上面这几道题,都用到了一个模板

  初始化查找区间

  查找到mid值

  判断mid值是否符合条件

  best保存当前最优值

  更改查找区间,寻找是否有更优值

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值