二分算法 cpp


7. 二分算法

  • 基础算法中最难的
  • 原理与模板简单
  • 难点在细节
  • 处理边界问题
  • 解集中存在二段性

模板题 :

!leetcode

34. 在排序数组中查找元素的第一个和最后一个位置

中等

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

**输入:**nums = `5,7,7,8,8,10]`, target = 8 **输出:**\[3,4

示例 2:

**输入:**nums = `5,7,7,8,8,10]`, target = 6 **输出:**\[-1,-1

示例 3:

**输入:**nums = \[\], target = 0
输出:-1,-1

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109
思路:

题目要求 -> 找到 起始位置 和 初始位置

  1. 方一 -> 暴力解法 -> 从前往后扫描数组 -> O(n)

    暴力解法慢在没有利用数组的有序性

  2. 方二 -> 二分算法

    1. 查找起始位置

      left---------------mid---------------right

      a[mid] >= t ; right = mid ; [left, right];
      a[mid] < t ; left = mid + 1 ; [left, right]

      细节问题:

      1. while循环的判断条件

        while ( left < right ); √

        while ( left <= right );

        不能=, 可能会死循环 , eg: [2,2]

      2. 求中点的方法

        方一 : ( left + right ) / 2 -> 奇数中点靠左 √

        方二 : ( left + right + 1 ) / 2 -> 奇数中点靠右 -> 死循环 -> [2, 2]

      3. 二分结束后, 相遇点的情况(容易忽略)

        判断循环结束后的结果是否为我们所需

    2. 查找终止位置

      left---------------mid---------------right

      a[mid] <= t; left = mid ; [left, right]
      a[mid] > t ; right = mid - 1; [left, right]

      细节问题:

      1. while循环的判断条件

        while ( left < right ); √

        while ( left <= right );

        不能=, 可能会死循环 , eg: [2,2]

      2. 求中点的方法

        方一 : ( left + right ) / 2 -> 奇数中点靠左 -> 死循环 -> [2, 2]

        方二 : ( left + right + 1 ) / 2 -> 奇数中点靠右 √

      3. 二分结束后, 相遇点的情况(容易忽略)

        判断循环结束后的结果是否为我们所需

代码:(二分模板)
cpp 复制代码
//二分查找区间的左端点
int l = 1, r =n;
while(l < r)
{
	int mid = (l + r) /2;
	if(check(mid)) r = mid;
	else l = mid + 1;
}

//二分结束后可能需要判断是否存在结果
cpp 复制代码
//二分查找区间的右端点
int l = 1, r = n;
while(l < r)
{
	int mid = ( l + r + 1 ) / 2;
	if(check(mid)) l = mid;
	else r = mid - 1;
}
//二分结束之后可能需要判断是否存在结果
  • 有时会溢出( l+r > int的范围 ), 可采用

    mid = l + ( r - l ) / 2 ;
    mid = l + ( r - l + 1 ) / 2 ;

    或者用 long long 定义

  • 模板不要死记 , 具体题目具体分析

  • 时间复杂度 ---> 二分的次数 ---> O ( l o g N ) O(log N) O(logN)

  • STL 中的二分查找
    <algorithm>

    • lower_bound : 大于等于 x 的最小元素, 返回迭代器 , O ( l o g N ) O(log N) O(logN)
    • uooer_bound : 大于 x 的虽小元素, 返回迭代器, O ( l o g N ) O(log N) O(logN)

    使用方法:
    auto it = lower_bound(左端点 闭区间,右端点 开区间, 数字);
    auto it = lower_bound(a+1 , a + n + 1 , target) ;
    int a = *it -> 解引用

    均采用二分查找实现, 但STL中的这个仅仅只适用于"在有序的数组中查找" , 二分答案不能用


7.2 二分答案

7.2.1 木材加工(二分答案模板题目)

!洛谷

P2440 木材加工

题目背景

要保护环境。

题目描述

木材厂有 n n n 根原木,现在想把这些木头切割成 k k k 段长度均 为 l l l 的小段木头(木头有可能有剩余)。

当然,我们希望得到的小段木头越长越好,请求出 l l l 的最大值。

木头长度的单位是 cm \text{cm} cm,原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。

例如有两根原木长度分别为 11 11 11 和 21 21 21,要求切割成等长的 6 6 6 段,很明显能切割出来的小段木头长度最长为 5 5 5。

输入格式

第一行是两个正整数 n , k n,k n,k,分别表示原木的数量,需要得到的小段的数量。

接下来 n n n 行,每行一个正整数 L i L_i Li,表示一根原木的长度。

输出格式

仅一行,即 l l l 的最大值。

如果连 1cm \text{1cm} 1cm 长的小段都切不出来,输出 0。

输入输出样例 #1

输入 #1

复制代码
3 7
232
124
456

输出 #1

复制代码
114

说明/提示

数据规模与约定

对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 10 5 1\le n\le 10^5 1≤n≤105, 1 ≤ k ≤ 10 8 1\le k\le 10^8 1≤k≤108, 1 ≤ L i ≤ 10 8 ( i ∈ 1 , n ) 1\le L_i\le 10^8(i\in1,n) 1≤Li≤108(i∈1,n)。

思路:
  1. 方一 : 暴力解法

    1. 枚举所有的切割长度 x

    2. 求 x 情况下 , 能切出来几段

  2. 方二 : 利用二分优化方一

    1. x -> 切割出小段的长度 c -> 在 x 的基础下 , 最多能切出的段数 k -> 最终要切出的段数

    2. 设 ret 为最终结果(段数) c >= k;

      ±-------------±--------------+

      left = 0 mid right = maxlen

代码:
cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 +10;
typedef long long LL;

LL n, k;

LL l[N];

LL calc(LL x)
{
	LL cnt = 0;
	for(int i =1; i<= n; i++)
	{
		cnt += l[i] / x;
	}
	
	return cnt;
}

int main()
{
	cin >> n >> k;
	
	for(int i = 1; i <= n; i++) cin >> l[i];
	
	int left = 0 , right = 1e8;
	while(left < right)
	{
		LL mid = (left + right + 1) / 2;
		
		if(calc(mid) >= k) left = mid;
		else right  = mid - 1;
	}
	
	cout << left;
	
	return 0;
 } 

相关推荐
开开心心就好3 小时前
白天晚上自动调蓝光,护眼工具亮度可调
服务器·开发语言·算法·pdf·ocr·intellij-idea·idea
Jsundoku4 小时前
第六章 机器学习 -- 支持向量机
算法·机器学习·支持向量机
?abc!4 小时前
(数组)448. 找到所有数组中消失的数字
算法
淡海水4 小时前
05-03-源码-Boehm GC分配与碎片管理
开发语言·算法·unity·c#·游戏引擎·gc
青少儿编程课堂4 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C/C++考级:任意进制互转与大数多进制加法解析
c++·python·算法·bfs·信息学竞赛
多弗朗皮卡丘5 小时前
C++ 哈希(下)
c++·算法·哈希算法
aichitang20245 小时前
Meta Muse Spark Open:816B 巨型开放权重多模态 Agent 模型架构、部署实战与社区首测全解
大数据·人工智能·python·算法·ai·架构·spark
Coder-magician5 小时前
图论总结篇
算法·图论
有同事要进步6 小时前
Leecode第68题:x的平方根
java·数据结构·算法