当前位置: 首页 > news >正文

蓝桥备赛(27)算法篇【二分算法】

一 、 二分算法

二分算法的难点在于对各种各样的细节问题 。所以 , 就算你把二分算法的模板背熟了,但是忽略了各种乱七八糟的边界问题 , 也很难全都AC

那么,我借助下面的OJ题 , 说明一下 需要处理的一些小细节:

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

在查找起始位置的时候 , 需要处理一些细节问题:

  1. while 循环里面的判断如何写?
  2. 求中点的方式?
  3. 二分结束后 , 相遇点的情况是什么?

需要考虑清楚 , 分析明白 , 不要带有未经过 证实的惯性思维!!!!

在查找 终止位置的时候 , 需要处理一些细节问题:

  1. while 循环里面的判断如何写?
  2. 求中点的方式?

需要考虑清楚 , 分析明白 , 不要带有未经过 证实的惯性思维!!!!

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int n = nums.size();
        //处理一下边界情况
        if(n == 0)return{-1,-1};

        //1.求起始的位置
        int left = 0,right = n-1;
        while(left < right)
        {
            int mid = (left + right) / 2;
            if(nums[mid] >= target)right = mid;
            else left = mid+1;
        }

        //left 或者 right 所指的位置就有可能是最终结果
        if(nums[left]!=target)return{-1,-1};
        int retleft = left;//记录起始位置

        //2.求终止位置
         left = 0,right = n-1;
        while(left<right)
        {
            int mid = (left + right+1)/2;
            if(nums[mid]<=target)left = mid;
            else right = mid-1;
        }

        int retright = right;//记录最终位置
        return {retleft,retright};
    }
};

借助上面的题目 , 我们了解了二分算法,这里总结一下:

当我们的解具有二段性的时候 , 就可以使用二分算法找出答案:

1)根据待查找区间的中间位置 , 分析答案会出现在那一侧;

2)接下来舍弃一半的待查找区间  ,转而在有答案的区间内继续使用二分查找结果!

二分的模板在网上至少能搜出来三个以上。但是,我们仅需要掌握一个,并且一直使用下去即可。

下面介绍一种,其他的可以去搜搜看~

1 . 为了防止溢出,求中点时 可以使用下面的方式:

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

2. 时间复杂度 : 每次二分都会去掉一般的查找区域(就是 嘎掉一般的数据了...) , 因此时间复杂度 为 log N

3. 【怎么记忆模板?】

 不用死记硬背 , 算法原理搞清楚之后 , 在分析题目的时候 , 自然而然就知道怎么写二分的代码了 , 而且 , 在不同的题目 , 边界情况也是不同的 , 需要具体情况具体分析。e

4. 【二分问题解决流程】

1)先画图分析,确定使用左端点模板 还是 右端点模板 , 还是两者配合一起使用

2)二分出结果之后 , 不要忘记判断结果是否存在 , 二分问题细节很多 , 一定一定一定要分析全面!!!

5. 【STL中二分查找】

<algorithm>

1) lower_bound : 大于等于 x 的最小元素 , 返回的是迭代器 , 时间复杂度:O(log N)

2) upper_bound : 大于 x 的最小元素 , 返回的是迭代器 , 时间复杂度:O(log N)

二者均采用二分实现。但是STL中的二分查找 , 只能适用于 “ 在有序的数组中查找 ” ,如果是二分答案就不能使用 。 

二、二分查找

2.1 牛可乐与魔法封印

登录—专业IT笔试面试备考平台_牛客网

#include <iostream>

using namespace std;

const int N = 1e5 + 10;
int a[N];
int n,q;

int binary_search(int x,int y)
{
	//1.找大于等于x 的起始位置
	int left = 1,right = n;
	while(left < right)
	{
		int mid = (right + left)/2;
		if(a[mid] >= x) right = mid;
		else left = mid + 1;	
	} 
	//处理数组中不存在大于等于 x 的情况
	if(a[left] < x) return 0;
	int tmp = left;
	
	//2. 找小于等于y 的终止位置
	left = 1,right = n;
	while(left < right)
	{
		int mid = (left + right + 1 )/2;
		if(a[mid] <= y) left = mid;
		else right = mid - 1;
	} 
	//处理数组中不存在小于等于 y 的情况
	if(a[left] > y) return 0;
	return left-tmp+1;
}

int main()
{
	cin >> n;
	//输入 
	for(int i = 1 ;i<=n;i++)cin >> a[i];
	
	//q次询问
	cin >> q;
	while(q--)
	{
		int x,y;
		cin >> x >> y;
		cout << binary_search(x,y) << endl;	
	} 
	return 0;
}

2.2 A-B数对

P1102 A-B 数对 - 洛谷

这里我们用一下STL :

1) lower_bound: 传入要查询区间的左右迭代器 (注意是左闭右开的区间 , 如果是数组就是左右指针 ) 以及要查询的值 k  , 然后返回该数组中 >= k 的第一个位置;

2)upper_bound: 传入要查询区间的左右迭代器 (注意是左闭右开的区间 , 如果是数组就是左右指针 ) 以及要查询的值 k  , 然后返回该数组中 > k 的第一个位置;

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

typedef long long LL;
const int N = 2E5 + 10;
LL a[N];
LL n,c;

int main()
{
	cin >> n >> c;
	for(int i = 1;i<=n;i++)cin >> a[i];
	
	//1.排序 
	sort(a+1,a+1+n);
	
	//2.b = a - c
	LL ret = 0;
	for(int i = 2 ; i<= n ;i++)
	{
		LL b = a[i] - c;
		ret += upper_bound(a+1,a+i,b) - lower_bound(a+1,a+i,b);
	 } 
	 cout << ret << endl;	
	return 0;
}

2.3 烦恼的高考志愿

P1678 烦恼的高考志愿 - 洛谷

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

typedef long long LL;
const int N = 1e5 + 10;
LL a[N];;
int n,m;

int find(LL x)
{
	int left = 1,right = m;
	while(left < right)
	{
		int mid = (left + right)/2;
		if(a[mid] >= x)right = mid;
		else left = mid + 1;
	}
	return left;
}

int main()
{
	cin >> m >> n;
	for(int i = 1;i<=m;i++)cin >> a[i];
	
	//1.排序
	sort(a+1,a+m+1);
	
	//2.二分
	//加上左右护法
	a[0] = -1e7 + 10;
	 
	LL ret = 0;
	while(n--)
	{
		LL x;
		cin >> x;
		int pos = find(x);
		ret += min(abs(a[pos] - x),abs(a[pos-1] - x));	
	} 
	cout << ret << endl;
	return 0;
}

三、二分答案

准确来说,应该叫做【二分答案 + 判断】

3.1 木材加工

P2440 木材加工 - 洛谷

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

typedef long long LL;
LL n,k;
const int N = 1e5 + 10;
LL a[N];

//当切割长度为 x 的时候,最多能切除多少段 
LL calc(LL x)
{	
	LL ret = 0;
	for(int i = 1;i<=n;i++)
	{
		ret += a[i]/x;	
	}
	return ret;
}
int main()
{
	cin >> n >> k;
	//1.输入 
	for(int i = 1;i<=n;i++)cin>>a[i];
	
	//2.排序
	sort(a+1,a+1+n);
	
	//3.二分
	LL left = 0,right = 1e8;
	while(left < right)
	{
		LL mid = (left + right + 1 )/2;
		LL c = calc(mid);
		if(c >= k) left = mid;
		else right = mid-1;
	}
	cout << left << endl;
	return 0;
}

3.2 砍树

P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷

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

typedef long long LL;
const int N = 1e6 + 10;
LL n,m;
LL a[N];

LL calc(LL x)
{
	LL cnt = 0;
	for(int i = 1;i<=n;i++)
	{
		if(a[i] > x)cnt += a[i] - x;	
	}
	return cnt;
}

int main()
{
	cin >> n >> m;
	//1.输入 
	for(int i = 1;i<=n;i++)cin >> a[i];
	
	//2.排序
	sort(a+1,a+1+n);
	
	//3.二分
	LL left = 1,right = 2e9;
	while(left < right)
	{
		LL mid = (left + right + 1) / 2;
		LL c = calc(mid);
		if(c >= m)left = mid;
		else right = mid - 1;		
	} 
	cout << left << endl;
	return 0;
}

3.3 跳石头

P2678 [NOIP 2015 提高组] 跳石头 - 洛谷

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

typedef long long LL;
const int N = 5e4 + 10;
LL l,n,m;
LL a[N];

//当最短跳跃距离为x , 所能移走的岩石数 
LL calc(LL x)
{
	LL ret = 0;
	for(int i = 0;i<=n;i++)
	{
		int j = i+1;
		while(j <= n && a[j] - a[i] < x)j++;
		ret += j-i-1;
		i = j-1;
	}
	return ret;
}
int main()
{
	cin >> l >> n >> m;
	//1.输入
	for(int i =1 ;i<=n;i++)cin >> a[i];
	a[n + 1] = l;
	n++;
	
	//2.二分
	LL left = 1,right = l;
	while(left < right)
	{
		LL mid = (left + right + 1) /2;
		if(calc(mid) <= m)left = mid;
		else right = mid - 1;	
	}  
	 cout << left << endl;
	return 0;
}

相关文章:

  • 【赵渝强老师】达梦数据库的线程结构
  • 若依——基于AI+若依框架的实战项目(原理篇)
  • 23种设计模式-装饰器(Decorator)设计模式
  • C++类与对象-3.23笔记
  • 【Java】Springboot集成itextpdf制作pdf(内附pdf添加表格、背景图、水印,条形码、二维码,页码等功能)
  • 牛客春招刷题训练营 3月25日 Java 查找两个字符串a,b中的最长公共子串 构造C的歪
  • 基于docker-compose 部署可道云资源管理器
  • 系留无人机照明芯片迎来革新:80V耐压输入+FP7195千瓦级大功率调光IC方案落地
  • GitLab 中文版17.10正式发布,27项重点功能解读【三】
  • html方法收集
  • Java 集合操作详解与使用指南
  • UE4学习笔记 FPS游戏制作14 发射抛物线子弹
  • 多维动态规划 力扣hot100热门面试算法题 面试基础 核心思路 背题
  • 第四届能源、电力与电气国际学术会议(ICEPET 2025)
  • 后缀表达式 | 第十届蓝桥杯省赛C++B组
  • 如何使用Xshell连接Linux虚拟机
  • K8S学习之基础五十:k8s中pod时区问题并通过kibana查看日志
  • android Firebase Cloud Messaging (FCM) 接入
  • # 用 Paramiko,轻松掌控远程服务器
  • 0基础 | 制作麦克风音频放大电路
  • 春决火爆的背后,PEL如何做大这块电竞蛋糕
  • 让中小学生体验不同职业,上海中高职院校提供超5万个体验名额
  • 俄方确认普京与特朗普将于今晚通话
  • 国新办10时将举行新闻发布会,介绍4月份国民经济运行情况
  • 广东信宜一座在建桥梁暴雨中垮塌,镇政府:未造成人员伤亡
  • 2025吉林市马拉松开跑,用赛道绘制“博物馆之城”动感地图