二分算法(知识点+题目)

二分算法

知识点

  • 二分的数组具有单调性

  • 查找某个值是否存在:在数组 a 中查找 target

C+ 复制代码
// a升序
bool find(int l, int r, int target, vector<int>&a)
{
    while(l <= r)
    {
        int mid = l + (r - l)/2; //防溢出
        if(a[mid]==target) 
        	return true;
        else if(a[mid]<target) 
        	l = mid + 1;
        else 
        	r = mid - 1;
    }
    return false;
}
  • 猜一个答案 mid ,写 bool check(mid) 函数,检查 mid是否可行,找最大值的最小,或者找最小值的最大

    复制代码
    bool check(int mid)
    {
        // 初始化变量
        
        // 遍历模拟逻辑
        
        // 最终判断条件
        return ;
    }

    求最小可行值(最大值最小)

    复制代码
    while(L <= R)
    {
        int mid = L + (R - L)/2;
        if(check(mid))
        {
            ans = mid;   // 可行,尝试更小的
            R = mid - 1;
        }
        else
        {
            L = mid + 1; // 不行,扩大
        }
    }
    cout << ans;

    求最大可行值(最小值最大)

    复制代码
    while(L <= R)
    {
        int mid = L + (R - L)/2;
        if(check(mid))
        {
            ans = mid;   // 可行,尝试更大
            L = mid + 1;
        }
        else
        {
            R = mid - 1; // 不行,缩小
        }
    }
    cout << ans;

题目练习

1、洛谷P1577切绳子

一、题意梳理

给定 n 根绳子(长度单位米,最多两位小数),切割出 k 根长度完全相等 的小段绳子,不能拼接绳子。 求小段绳子最长可以多长,结果保留两位小数、直接截断,不四舍五入。

二、核心算法:二分答案

  1. 单调性原理(二分使用前提)
  • 假设设定小段长度为 x:x 越小,所有绳子能切出来的总段数越多;x 越大,总段数越少。
  • 存在分界点:存在一个最大长度 ans,满足长度≤ans 时可以切出≥k 段;长度 > ans 时无法切够 k 段。满足二分单调性。
  1. 小数处理技巧(代码选择整数二分)

    输入的长度最多两位小数,把所有长度 ×100,单位转为厘米,全部变成整数,彻底消除浮点数精度误差。

  2. Check 逻辑(代码内直接展开,无单独函数)

    对于当前尝试长度 mid(厘米): 遍历每根绳子,绳子长度 / mid 得到这根绳子能切出多少段; 累加所有段数,如果总和大于等于 k,说明 mid 这个长度可行

三、最大化可行值

  1. 边界设定
    • 左边界 l = 1
    • 右边界 r = 所有绳子里最长的长度(厘米)
  2. 循环条件 l<=r
    • mid = (l + r) / 2,尝试当前长度
    • 如果可行:记录答案 ans=mid,尝试更长的长度 l = mid + 1
    • 如果不可行:长度太长,缩小范围 r = mid - 1
  3. 循环结束后,ans 就是最大可行长度(单位厘米),除以 100 转为米输出。

四、代码

复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long 
signed main(){
    int n,k;
    cin>>n>>k;
    vector<int>a(n);
    double t;
    int r=0;
    for(int i=0;i<n;i++){
        cin>>t;
        a[i]=(int)(t*100);
        r=max(a[i],r);
    }
    int l=1;
    int ans=0;
    while(l<=r){
        int mid=(r+l)/2;
        int c=0;
        for(int i=0;i<n;i++){
            int s=a[i];
            c=c+s/mid;
        }
        if(c>=k){
            ans=mid;
            l=mid+1;
        }
        else{
            r=mid-1;
        }
    }
    printf("%.2f",ans/100.0);
    return 0;
}

2、洛谷P1843 奶牛晒衣服

题目背景

熊大妈决定给每个牛宝宝都穿上可爱的婴儿装 。但是由于衣服很湿,为牛宝宝晒衣服就成了很不爽的事情。于是,熊大妈请你(奶牛)帮助她完成这个重任。

题目描述

一件衣服在自然条件下用一秒的时间可以晒干 aaa 点湿度。抠门的熊大妈只买了一台烘衣机。使用一秒烘衣机可以让一件衣服额外烘干 bbb 点湿度(一秒晒干 a+ba+ba+b 湿度),但在同一时间内只能烘一件衣服。现在有 nnn 件衣服,第 iii 衣服的湿度为 wiw_iwi(保证互不相同),要你求出弄干所有衣服的最少时间(湿度为 000 为干)。

输入格式

第一行三个整数,分别为 n,a,bn,a,bn,a,b。

接下来 222 到 n+1n+1n+1 行,第 iii 行输入 wiw_iwi。

输出格式

一行,弄干所有衣服的最少时间。

输入 #1

复制代码
3 2 1
1
2
3

输出 #1

复制代码
1

说明/提示

样例解释

让机器烘第三件衣服即可一秒完成。

数据范围

1≤wi,a,b,n≤5×1051 \le w_i,a,b,n \le 5 \times 10^51≤wi,a,b,n≤5×105

解题思路

一、题意梳理

  1. 一共有 n 件衣服,每件初始湿度 c[i]
  2. 自然风干:每秒所有衣服同时降低 a 点湿度;
  3. 烘干机 :同一时刻只能烘一件衣服,被烘的衣服每秒额外降低 b 点湿度;
  4. 目标:求出晒干全部衣服需要的最少总时间

一件正在烘干的衣服每秒总共减少湿度:(a+b)

没有烘干的衣服每秒只减少湿度:a

二、二分答案思想

答案具有单调性:

  • 如果 T 秒可以晒干所有衣服,那么任何大于 T 的时间一定也可行;
  • 如果 T 秒不能晒干,任何小于 T 的时间一定不可行。

满足单调性 → 可以使用二分答案

我们二分猜测总时间 mid,写 check 函数验证:只用 mid 秒能不能把所有衣服晒干

三、check (mid) 检验逻辑(核心)

假设总时长为 mid

  1. 对第 i 件衣服:只依靠自然风干 mid秒,总共减少 a*mid 湿度。

    • (c[i] <= a * mid):单纯风干就能干透,不需要烘干机;
    • (c[i] > a * mid):自然风干不够,必须使用烘干机弥补差额。
  2. 设差额: need = c[i] - a * mid;

    烘干机每秒额外消除 b 湿度,这件衣服至少需要烘干时长:need / b

    (向上取整,不足 1 秒按 1 秒计算)

  3. 把所有衣服需要的烘干时长累加得到 sum

限制条件:烘干机同一时间只能烘一件,所以全部衣服需要的烘干总时长 sum ≤ mid,才代表存在可行安排方案。

  • sum ≤ mid:mid 秒可行;
  • sum > mid:mid 秒不可行。

四、二分边界与更新规则

  • 左边界 (l=1),右边界 (r=1e9)(足够覆盖最大可能答案)

    采用模板:寻找满足条件的最小时间

五、代码

复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

const int MAXN = 600010;  //数组最大容量(衣服件数上限)
int c[MAXN];              //全局数组,防止栈溢出

signed main(){
    int n,a,b;
    cin>>n>>a>>b;
    for(int i=0;i<n;i++){
        cin>>c[i];
    }

    int l=1,r=1e9; //l、r是时间二分左右边界
    while(l<r){
        int mid=(l+r)/2;
        int sum=0; //累计总共需要占用烘干机的时间
        for(int i=0;i<n;i++){
            //仅靠自然风干mid秒就能晾干,不需要烘干机
            if(c[i] <= a * mid){
                continue;
            }
            //need:需要烘干机额外消除的湿度
            int need = c[i] - a * mid;
            //向上取整,计算这件衣服最少烘干时长
            if(need % b == 0){
                sum += need / b;
            }else{
                sum += need / b + 1;
            }
        }
        //判定:总烘干时长不超过总时间mid,代表可行
        if(sum <= mid){
            r = mid;    //可行,尝试更小时间
        }else{
            l = mid + 1;//不可行,增大时间下界
        }
    }
    cout<<l;
    return 0;
}

3、萌新赛(三)I-遗迹核心的临界容差

链接:https://ac.nowcoder.com/acm/contest/138881/I

来源:牛客网

在星陨荒漠的深处,考古队发掘出了一条由 NNN 座储能核心组成的远古供能矩阵。这些核心一字排开,每一座核心都封存着一个整数灵压强度值 Ai

相邻两座核心之间由能量导管连接。如果这两座核心的灵压值之差的绝对值超过了一个阈值 X,那么这段导管就会产生能量扰动,导致它所在的那一段连续回路整体变得不稳定。换句话说,一段连续的核心序列是稳定的,当且仅当这段序列里任意相邻两座核心的灵压差都不超过 X

为了维持整体稳定,工程部可以在任意两座相邻核心之间插入一个空间隔离锚,隔离锚会切断该处的能量连接。这样一来,整条矩阵就被分割成若干个连续且相互隔绝的子段。只要每一个子段内部都是稳定的,整条矩阵就可以正常运转。

现在考古队想知道,最小的非负整数阈值 X 是多少,使得他们只需要插入不超过 K−1 个隔离锚(也就是把序列分成不超过 K 段),就能让所有子段都满足稳定条件。

输入描述:

复制代码
第一行包含两个整数 NNN 和 KKK,分别代表核心总数和允许的最大分段数。
第二行包含 NNN 个整数 A1,A2,...,AnA_1, A_2, \dots, A_nA1,A2,...,An,依次为每座核心的灵压强度值。

输出描述:

复制代码
输出一个整数,表示满足条件的最小阈值 X。

输入

复制代码
6 3
2 5 1 9 8 10

输出

复制代码
3

解题思路

最多切 K 刀进行分段;

一段内部不能出现大于 mid 的差值。

只要某一处差值 > mid,这里必须切一刀。

设:

c = 需要切割的次数

初始 (c=1)(默认 1 整段,每切一刀段数 + 1)

如果需要切割次数 c>k,代表当前 mid 太小,不满足条件。

目标:找到满足条件的最小 mid(最大值最小经典二分答案)

代码

复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
vector<int>a;
int l=0,r=1e9;
int n,k;

// check:猜测阈值mid,判断是否可行
bool check(int mid){
    int c=1;   // 初始整体是1段
    // a内存放所有相邻元素差值
    for(int i=0;i<a.size();i++){
        // 当前差值超过阈值,必须切一刀,段数+1
		if(a[i]>mid){
			c++;
		}
	}
	// 需要的总段数 > 允许的最大段数k → 不可行
	if(c>k){
		return false;
	}
	else{
		return true;
	}
}

signed main(){
    cin>>n>>k;
    // 只有1个核心,没有相邻差值,答案为0
    if(n==1){
        cout<<0;
        return 0;
    }
    int b[n+3]; // 存储原始灵压数组
    for(int i=0;i<n;i++){
        cin>>b[i];
    }
    // 计算所有相邻两个灵压的差值绝对值,存入a
    for(int i=0;i<n-1;i++){
        int num=abs(b[i+1]-b[i]);
        a.push_back(num);
    }
    int ans=0;
    // 二分框架:求最小可行mid
    while(l<=r){
        int mid=l+(r-l)/2; // 防溢出写法
        if(check(mid)){
            ans=mid;    // mid可行,记录答案
            r=mid-1;    // 尝试寻找更小的合法阈值
        }    
        else{
            l=mid+1;    // mid不可行,放大阈值
        }
    }
    cout<<ans;
    return 0;
}
相关推荐
蒙奇·D·路飞-3 小时前
OpenClaw:开源的《合金弹头》精神续作与终极本地部署指南
c++·开源·mfc
AndrewHZ4 小时前
【LLM技术全景】阶段总结:技术原理篇核心知识回顾
人工智能·深度学习·算法·语言模型·大模型·llm·芯片开发
小星星闪亮登场5 小时前
2026萌新联赛第三场-- (郑州轻工业大学)
数据结构·c++·经验分享·算法·贪心算法·排序算法·深度优先
梓䈑5 小时前
【用 Vibe Coding 实现的 C++17 在线判题系统】前端开发 + Web 自动化测试
前端·c++·ai编程
冻柠檬飞冰走茶5 小时前
PTA基础编程题目集 7-8超速判断(C++语言实现)
开发语言·数据结构·c++·算法
玖玥拾6 小时前
LeetCode 88 合并两个有序数组
算法·leetcode
TunerT_TQ6 小时前
Valhalla 静态工程审阅 #018|MMCV 源码证据驱动评测【大厂开源基础设施特辑】
c++·开源·mfc·#计算机视觉·#商汤科技·#openmmlab·#静态源码审计
数据皮皮侠AI6 小时前
上市公司数字供应链金融指数(2010-2024)
大数据·人工智能·算法
小程故事多_806 小时前
用GRPO算法重塑多智能体系统,从原理落地到复杂任务规划实战
人工智能·算法