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

蓝桥杯专项复习——二分查找、二分答案

目录

二分查找、二分答案基础知识

二分查找模版

【模版题】数的范围

借教室


二分查找、二分答案基础知识

二分模版

二分查找

【模版题】数的范围

输入样例

6 3
1 2 2 3 3 4
3
4
5

输出样例

3 4
5 5
-1 -1

思路:

对应两个模版,起始位置是对应第一个模版,即后面的都符合
终止位置对应第二个模拟,即前面的符合

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

const int N=100000+10;

int a[N];

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int n,q;cin>>n>>q;
	for(int i=0;i<n;i++)
		cin>>a[i];
	while(q--)
	{
		int x;cin>>x;
		int l=0,r=n-1;
		while(l<r)//查找起始位置,右边符合 
		{
			int mid=(l+r)/2;
			if(a[mid]>=x)
				r=mid;
			else
				l=mid+1;
		}
		if(a[l]!=x)//不存在
			cout<<"-1 -1"<<endl;
		else//查找终止位置 
		{
			cout<<l<<' ';//将起始位置输出 
			l=0,r=n-1;
			while(l<r)
			{
				int mid=(l+r+1)/2;
				if(a[mid]<=x)
					l=mid;
				else
					r=mid-1;
			}
			//不用再判断是否存在 
			//输出终止位置 
			cout<<l<<endl;
		 } 
	}
	return 0;
 } 

借教室

输入样例1

4 3
2 5 4 3
2 1 3
3 2 4
4 2 4

输出样例1

2

说明1

第一份订单满足后,这四天剩余的教室数为{0,3,2,3}
第二份订单要求第二天到第四天每天提供3个教室,而第三天剩余的教室数为 2,
因此无法满足。分配停止,通知第二个申请人修改订单。

输入样例2

4 1
2 5 4 3
2 1 3

输出样例2

0

思路:

遍历订单,再对所遍历到的订单从第一个1开始进行判断。

如果直接枚举每一个订单是O(n),再判断又是O(n),时间复杂度就会是O(n^2),会超时,需要优化。

优化:

二分和差分的结合
即通过二分来搜索不符合的点,使用差分实现check函数的判断

为什么使用二分:要求找到不符合的订单是搜索,并且如果前面的订单不满足后面的订单就直接取消,具有二段性,可以使用二分法,时间复杂度变为nlogn

为什么使用差分:一段时间其实就可以看做是一段区间,对于每个教室的申请,其实就是区间修改,完全符合差分条件

 满足的条件是申请教室小于空闲教室,模版1符合
本题重点其实还是差分,二分只是优化了时间复杂的,实际的判断功能是通过差分实现。

表示是否全部分配完的方案:即特判
令r=m+1,表示有m个订单,当退出循环时l=m+1表示所有m个订单都可分配教室,就输出0;如果r=m,结束当l=m时不清楚第m个订单是否分配到

#include<iostream>
#include<bits/stdc++.h>
#define int long long 

using namespace std;

const int N=1000000+10;
int n,m;
int a[N],b[N];
int c[N],l[N],r[N];

bool check(int x)
{
	//对每次的订单都是从1开始到x,且内容都可以实现更新 
	for(int i=1;i<=n;i++)
		b[i]=a[i]-a[i-1];//构建a的差分数组
		
	//实现对a数组的特定范围都减去c
	for(int i=1;i<=x;i++)//对第1~x个订单进行操作 
	{  
		b[l[i]]-=c[i];
		b[r[i]+1]+=c[i];
	 } 
	for(int i=1;i<=n;i++)
	{
		b[i]+=b[i-1];//将差分数累加,其实是a[i] 
		if(b[i]<0)//如果a[i]<0相当于教室数不足
			return true; 
	}
	return false;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i]; //每天可借的订单数
		
	for(int i=1;i<=m;i++)
	 //订单数据 ,这里先导入是为了后面使用差分对订单进行判断 
		cin>>c[i]>>l[i]>>r[i];
	
	//题目是要求找到不符合的订单而且订单具有二段性
	//最快的方法是使用二分进行查找 
	int l=1,r=m+1;//对订单进行遍历  
	while(l<r)
	{
		int mid=(l+r)/2;
		//判断第1~第mid个订单是否符合题意
		//这里是将申请教室小于空闲教室作为答案
		//即左边为所符合的答案,使用模版1 
		if(check(mid)) //当教室不足时为true,r=mid即将该订单舍去,遍历前面的订单 
			r=mid;
		else //当1~mid订单符合时左范围增大,使得查询订单数向后挪 
		//当所有都符合时l==r==m+1 
			l=mid+1; 
	 } //结束条件是l==r 
	if(l==m+1)
		cout<<'0'<<endl;
	else
		cout<<r<<endl;
	return 0;
 } 

二分答案

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

思路:

由题意可知,只要所定高度h足够小,都能砍到所需的木材,所以是要在所有较小数中寻找到一个最大数,这里可以看出具有二分性质,可用二分进行查找,并且此时符合模版2(左边较小的数都符合)

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

const int N=1000000+10;

int n,m;
int h[N];
int l,r;
int tmp;

bool check(int x)
{
	int sum=0;
	for(int i=0;i<n;i++)
		sum+=max(tmp,h[i]-x);//这里需要使用max函数,因为当树的高度小于所定高度时会是负数 
	return sum>=m;// 集合模版图,左边x小的都是成立,找一个最大的较小数 
	
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n>>m;
	for(int i=0;i<n;i++)
		cin>>h[i];
		
	l=0,r=1e10;
	while(l<r)
	{
		int mid=(l+r+1)/2;
		if(check(mid))
			l=mid;
		else
			r=mid-1;
	}
	cout<<l<<endl;
	return 0;
 } 

 P3743 小鸟的设备

具有二段性,即小于等于答案的秒数都满足,所有大于秒数都不满足,所以使用二分,但是当类型是double(因为我们这里是对时间进行二分,且题目有精度要求)时不再区分模版1还是模版2,而且在二分判断循环中,if函数后面是l或者r都不影响

 思路:

特判输出-1的情况:如果充电速率大于消耗的速率,一定可以无限使用

如何判断时间是否符合:(check函数内容
只要判断mid时间可以使用多少时间,即mid时间内产生的能量和它本身具有的能量是否大于消耗的能量,当产生的能量大于等于消耗的能量,更新l

 对于精度:

<=1^4表示精度有4位即可,大于1e-5时精度有5位,完全满足题目要求

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

const int N=100000+10;

int n;
double p;
int a[N],b[N];

bool check(double x)
{
	double power=p*x;//x时间内产生的能量 
	double sum=0;//x时间内消耗的能量 
	for(int i=0;i<n;i++)
	{
		if(a[i]*x>b[i])//如果消耗量大于之前存的能量
			sum+=a[i]*x-b[i]; 
	}
	return power>=sum;//当产生的能量大于等于其消耗能量时更新l 
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n>>p;
	double sum=0.0;
	for(int i=0;i<n;i++)
	{
		cin>>a[i]>>b[i];
		sum+=a[i];
	}
		
	if(p>=sum)//特判,此时能无限使用
	{
		cout<<"-1"<<endl;
		return 0;
	 } 
	double l=0,r=1e10;//二分查找,初始化范围
	while(r-l>=1e-5)//确保输出精度 
	{
		double mid=(l+r)/2.0;
		if(check(mid))
			l=mid;
		else
			r=mid;
	 } 
	 cout<<l<<endl; 
	return 0;
}

相关文章:

  • C++自定义迭代器
  • 【学习笔记】计算机网络(六)
  • [GESP202503 四级] 二阶矩阵
  • 初始ARM
  • 8.3链表专题:LeetCode 143. 重排链表
  • elementui的默认样式修改
  • 常见集合篇(二)数组、ArrayList与链表:原理、源码及业务场景深度解析
  • 【C语言】字符函数的易错点及其模拟实现
  • SQL在线格式化 - 加菲工具
  • WINDOWS 2019 2022 服务器安装了更新补丁 自动重启 分析
  • 第6章 与学习相关的技巧(鱼书)
  • (二)机器学习---常见任务及算法概述
  • 3.31 代码随想录第三十一天打卡
  • 第十章 VGA显示圆
  • # 使用 OpenCV 和神经网络实现图像风格化
  • AISEO中的JSON 如何部署?
  • 为 MinIO AIStor 引入模型上下文协议(MCP)服务器
  • kafka副本同步时HW和LEO
  • Linux驱动开发 中断处理
  • IP-PBX(IP专用交换机)
  • 客服外包网站/网站托管
  • 淘宝网发布网站建设/万网注册域名查询官方网站
  • 微信网站建设哪家好/百度搜索热度指数
  • 北京网站建设还公司/阿里巴巴推广
  • 徐州好点的做网站的公司有哪些/一键seo提交收录
  • 2017设计工作室做网站/湖南网站营销seo方案