二分算法 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\in[1,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;
 } 

相关推荐
xushichao19892 小时前
C++中的工厂模式高级应用
开发语言·c++·算法
2501_924952692 小时前
C++模块化编程指南
开发语言·c++·算法
qzhqbb2 小时前
差分隐私与大模型+差分隐私在相关领域应用的论文总结
人工智能·算法
2401_831920742 小时前
基于C++的爬虫框架
开发语言·c++·算法
MSTcheng.2 小时前
【优选算法必修篇——位运算】『面试题 01.01. 判定字符是否唯一&面试题 17.19. 消失的两个数字』
java·算法·面试
weixin_421922692 小时前
模板元编程性能分析
开发语言·c++·算法
2401_851272992 小时前
C++中的类型擦除技术
开发语言·c++·算法
Liu628882 小时前
C++命名空间使用规范
开发语言·c++·算法
tankeven2 小时前
【无标题】
数据结构·c++·算法