0
点赞
收藏
分享

微信扫一扫

二分法自我总结

zhongjh 2022-03-25 阅读 83
算法

文章目录


前言

当题目答案是一个值,且在固定范围内的话,就可以考虑二分法求解。


一、二分法的模板?

bool check(int mid){
	return ;//返回true或者false;
}
int l = ma , r = sum;
    while (l<r){
        int mid = (r + l) /2;
        if(check(mid)){//根据实际调整
            r = mid;
        }else{
            l = mid + 1;
        }
    }

二 对应题目

1.蓝桥杯【打包】

题目链接

在这里插入图片描述

那么check()函数可以这样写

bool check(int mid){
    int now = 0;//每一包礼物的当前总重
    int p = 1 , i = 0;
    for (int i = 0; i < n; i ++ ){
        
        if(t[i]>mid)return false;//如果有某单个礼物重量超过检测值,不可能分发成功
        
        now += t[i];
        
        //如果超过检测值,表示完成一包分发
        if(now > mid){
        
            now = t[i];
            p++;
        }
    }
    //如果可以分发给更少的人,则一定可以分给更多的人
    return p <= m;
    
}

这样就可以通过二分法找到答案。
下面为完整代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;



const int N = 100010;

int t[N];
int n , m ;


bool check(int mid){
    int now = 0;
    int p = 1 , i = 0;
    for (int i = 0; i < n; i ++ ){
        
        if(t[i]>mid)return false;
        
        now += t[i];
        
        if(now > mid){
            now = t[i];
            p++;
        }
    }
    
    return p <= m;
    
}


int main()
{
    cin >> n >> m;
    int sum = 0 ;
    int ma = 0;
    for (int i = 0; i < n; i ++ ){
        scanf("%d", &t[i]);
        sum+=t[i];
        ma = max(ma , t[i]);
    }
    //如果左值设为单个礼物的最大值,check()函数中的这个检查可以不写
    int l = ma , r = sum;
    while (l<r){
        int mid = (r + l) /2;
        if(check(mid)){
            r = mid;
        }else{
            l = mid + 1;
        }
    }
    cout << l;
}

2.蓝桥杯【和谐宿舍】

题目链接
在这里插入图片描述

bool check(int mid){
    int now = 0 , idx = 0;
    int p = 1 , i = 0;
    for (int i = 0; i < n; i ++ ){
   		//如果单个高度超过了判断的最大值,直接返回false
   		//如果主函数的l是从ma开始的,这行代码可以不加
        if(t[i]>mid)return false;
        
        //如果作为一块挡板时的高度
        now = max(now , t[i]);
        
        //面积大于检测值,即为一块挡板
        if(now * (i - idx + 1) > mid){
            idx = i;
            now = t[i];
            p++;
        }
    }
    
    return p <= m;
    
}

3.AcWing【社交距离I】

题目链接
在这里插入图片描述

我们遍历所有的间距,并对左右做好处理。完整代码如下

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

string s;

const int N = 200010;

int n , cnt;
int dis[N] ;
bool ll , rr;


bool check(int mid){
    int sum = 0;
    for (int i = 0; i <= cnt; i ++ ){
        if(i==0 && ll|| i== cnt && rr){
        	//边界一端可以放牛
            if(dis[i] >= 2 * mid){
                sum+=2;
            }if(dis[i] >= mid){
                sum++;
            }
        }else{
            if(dis[i] >= 3 * mid - 1){
                sum+=2;
            }if(dis[i] >= 2 * mid-1){
                sum++;
            }
        }
        if(sum>=2){
            return true;
        }
    }
    return false;
}



int main()
{
    cin >>n;
    getchar();
    cin>>s;
    
    for (int i = 0; i < n; i ++ )
    {
        if(s[i]=='0'){
            dis[cnt]++;
        }else{
            cnt++;
        }
    }
    if(dis[0])ll = true;
    if(dis[cnt])rr = true;
	//如果全为0 , 直接两边各放一头
    if(!cnt){
        cout << n - 1;
        return 0;
    }
    int mi = 100010;
    for (int i = 1; i < cnt; i ++ ){
        mi = min(mi , dis[i]);
    }
	
	//此处的右端点值为中间间隔的最小值加1,算上最两边的间距可能为0
    int l = 0 , r = mi + 1;
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if (check(mid))
            l=mid;
        else
            r=mid-1;
    }
    cout << l;
    
    return 0;
}
举报

相关推荐

0 条评论