二分法的习题

博客围绕算法中求最大最小问题展开,通过两个具体题目进行分析。一是求能量E的最小值,二是求切分正方形巧克力边长的最大值。均采用二分法解题,分析了二分法所需的二段性和单调性,还给出了边界条件和判断条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.

1.1题目分析:

        1.1.1题干分析:

                输出结果:能量E 

                输出范围:[1,1e5]

                当h>E    E = E - (h - E) = 2E - h

                当h<=E  E = E + E - h = 2E - h

                无论h与E的大小关系,都有E = 2E - h

        1.1.2.方法选用分析:

                求最大最小问题,依次考虑二分dfsDp贪心

                二分要考虑结果是否有二段性单调性

                二段性:能量 E = mid 时 , 能否使E保持大于零

                单调性:E_k成立时, E_k+1也成立

                可以采用二分法解题

        1.1.3.方法结题分析:

                边界:L = 0 , R = 1e5  

                判断条件 :能量E能否跳过,即为E始终大于0

                当E = mid 可以跳过 , 则最小值在左侧,所以R=mid ,则 mid = (l+r)>> 1     

                当E = mid 不能跳过 , 则最小值在右侧,且最小值不是mid,则L = mid +1 

                ​​

 1.2程序设计:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 100010 ;
int n;
int h[N];

//判断E能否跳过
bool check(int e){ 
    bool fleg = true ;
    for(int i=1;i<=n;i++)
    {
        e = 2*e - h[i] ;       //能量表达式
        if(e>=1e5) {           //能量大于边界,肯定能跳过。防止爆掉,break结束循环
            fleg = true;
            break;
        }
        if(e<0) {              //能量跳不过
            fleg=false;
            break;
        }
    } 
    return fleg ;
}

int main(){
    scanf("%d",&n);                                 //读取楼个数
    for(int i=1;i<=n;i++)  scanf("%d",&h[i]);       //读取楼高度
    
    //二分法
    int l =0 ,r= 1e5;                               //左右区间
    while(l<r)                                      //循环条件
    {
        int mid = (l+r)>>1;                         //二分
        if(check(mid)) r = mid ;                    //判断条件
        else l = mid + 1 ;
    }
    printf("%d\n",r);                               //输出结果
    return 0;
}

 2.

 

2.1题目分析:

        2.1.1.题干分析:

                输出结果:切分正方形巧克力的边长

                输出范围:[1,1e5]

                分巧克力只能切割,不能拼接!!!

                一块边长为H[i] 和 K[i] 的巧克力,分为边长为z的正方形,可分 ( H[i] / z ) * ( K[i] / z ) 块(向下取整)。

        2.1.2.方法选用分析:

                求最大最小问题,依次考虑二分dfsDp贪心

                二分要考虑结果是否有二段性单调性

                单调性:边长为z的正方形成立时, z-1 的正方形也成立

                二段性:边长 z = mid ,判断能否成立 

                可以采用二分法解题

       2.1.3.方法结题分析:

                边界:L = 1 (至少分成1 * 1的巧克力) , R = 1e5  

                判断条件 :按照边长为z的正方形切割得到的块数sum能否大于小朋友个数

                当 z = mid 成立 , 则最大值在右侧,所以 L = mid ,则 mid =(l + r + 1 )>> 1     

                当 z = mid 不成立 , 则最大值在左侧,且最大值不是mid,则 R = mid -1 

                ​​​​​​​​​

 2.2.程序设计:

#include <iostream>
#include <cstdio>
using namespace std;

const int N = 100010;
int n, f ;                   //n:巧克力个数 f:小朋友个数
int H[N], W[N] ;             //巧克力边长H[N],W[N]
int z ;                      //正方形边长

bool check(int z)
{
    int sum=0 ; 
    for(int i=0;i<n;i++)
    {
        sum = sum + ( H[i] / z ) * ( W[i] / z ) ;  //分巧克力块数
        if(sum >= f) return true;                  //足够分给f个小朋友
    }
    return false;
}

int main()
{
    scanf("%d %d",&n,&f);                              //读取n块巧克力 ,f个小朋友
    for(int i=0;i<n;i++)  scanf("%d %d",&H[i],&W[i]);  //n块巧克力的边长H和W
    
    //二分
    int mid;
    int l = 1 , r = N ;             
    while(l < r)
    {
        mid = l + r + 1 >> 1;
        if(check(mid)) l = mid ;
        else r = mid - 1 ;
    }
    printf("%d\n",r);
    return 0;
}

 3.

3.1题目分析:

        3.1.1.题干分析:   

 

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>

using namespace std ;

const int N = 2500010;

struct Sum {
    int s, c, d;
    bool operator< (const Sum &t) const
    {
        if(s != t.s) return s < t.s;
        if(c != t.s) return c < t.c;
        return d < t.d;
    }
}sum[N];

int n , m;

int main()
{
    cin >> n ;
    for(int c = 0; c*c <=n; c++){
        for(int d=c; c*c + d*d <=n; d++)  
            sum[m ++] = {c*d + d*d,c,d};
    }
    sort(sum,sum + m);   //排序
    
    for(int a=0; a*a<=n ; a++){
        for(int b=a; a*a + b*b <= n;b++)
        {
            int t = n - a*a - b*b ;
            //二分
            int l =0 ,r =m-1;
            while(l<r)
            {
                int mid= l+r >> 1;
                if(sum[mid].s >= t) r=mid;
                else l = mid+1;
            }
            if(sum[l].s == t)
            {
                printf("%d %d %d %d\n", a, b, sum[l].c, sum[l].d);
                return 0;
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值