蓝桥杯备赛 Day8 二分

二分

1.要点

选取左闭右闭,[left,right]
(1)二分查找:有序数组,且无重复元素寻找目标值key

int left=0,right=n-1,res=-1;//未找到返回-1
while(left<=right){
	int mid=left+((right-left)>>1); //注意右边括号,+号优先于>>
	if(a[mid]==key){
		res=mid;
		break;
	}
	else if(a[mid]<key){ //key在现在区间右侧
		left=mid+1;
	}
	else{ //key在现在区间左侧
		right=mid-1;
	}
}

(2)二分答案:广义的有序,查找某种条件的最大(最小值)(最大值最小化和最小值最大化),三个条件:

  1. 答案在一个固定区间内;
  2. 可能查找一个符合条件的值不是很容易,但是要求能比较容易地判断某个值是否是符合条件的;
  3. 可行解对于区间满足一定的单调性。换言之,如果x是符合条件的,那么有x+1或者x-1也符合条件。(这样下来就满足了上面提到的单调性)
int left=0,right=-1;
while(left<=right){
	int mid=left+((right-left)>>1);
	if(check(mid)) right=mid-1; //这边以最大值最小化举例,如果mid符合条件,说明它至少不是最小的,还可以再缩小区间,且固定区间是单调递增的
	else left=mid+1;
}
cout<<left<<"\n"; //输出left,而不是right

2.例题

2022 青蛙过河(二分答案最大值最小化)

找最小的跳跃距离y,是单调的;将总的区间划分为多个长度为y的子区间,只要每个子区间高度和(前缀和)大于等于2x即可(寻找目标target y的判断条件),所以为一个二分问题,left=1,right=n,如果mid满足判断条件,说明不是最小的跳跃距离,缩小右边界,如果mid不满足判断条件,说明跳跃距离不够缩小左边界,最终找到的target=left(或者right-1)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N=1e5+10;
int a[N],prefix[N],n,x;

bool check(int y){//是否满足任意长度为y的区间高度和大于等于2*x 
	//任意区间从1开始,n-1-y+1结束
	for(int i=1;i<=n-y;i++){
		//[i,i+y-1]区间和 
		if(prefix[i+y-1]-prefix[i-1]<2*x)	return false;
	} 
	return true;
}

int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>x;
	for(int i=1;i<=n-1;i++){
		cin>>a[i];
		prefix[i]=prefix[i-1]+a[i];
	} 
	int left=1,right=n-1,res=0;//左闭右闭
	while(left<=right){
		int mid=left+((right-left)>>1);
		if(check(mid)){
			right=mid-1;
			//res=mid;
		}
		else	left=mid+1;
	} 
	cout<<left;
	//cout<<res;
	return 0;
}
2023冶炼金属

不用二分(不是最小值最大化这种,因为x成立,x-1不一定成立,不是单调的),就是多个集合取交集,Vmin=max(),Vmax=min()

2017分巧克力

注意
(1)这题是最小值最大化,满足条件left=mid+1,所以最终输出left-1(永远比最终结果多一)或者right(可以看最后两种情况,left==right时,若mid满足条件,left=mid+1结束循环,若mid不满足条件,肯定比key多1,right=mid-1=key)

#include <bits/stdc++.h>

using namespace std;
typedef pair<int,int> PII; //或者直接开两个数组
const int N=1e5+10;
PII p[N];
int n,k;

bool check(int mid){
	int sum=0;
	for(int i=0;i<n;i++){
		sum+=(p[i].first/mid)*(p[i].second/mid);
		if(sum>=k)	return true;
	}
	return false;
}


int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>k;
	for(int i=0;i<n;i++){
		cin>>p[i].first>>p[i].second;
	}
	int left=1,right=1e5,mid,max,res=0;
	while(left<=right){
		mid=left+((right-left)>>1);
		if(check(mid)){
			left=mid+1;
			//res=mid;
		}
		else	right=mid-1;
	}
	cout<<right;
	//cout<<res;
	return 0;
}
2022技能升级

学习:
(1)暴力用max_element()函数获取地址,int pos=max_element-a获取索引可得40分,换成优先队列priority_queue可得60分
(2)满分为二分答案思想,但跟之前不一样,不是题目问什么就是寻找什么目标值,而要转换一下,寻找到一个能下降到的最低攻击力x,使得大于x的攻击力下降次数和<m次,x+1肯定也成立,所以为最大值最小化,得到x后可以得到下降次数,进而用等差数列得到大于x的攻击力和,再加上剩余次数*x即可
(3)加法和乘法时刻要想到显示转换为long long

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m,a[N],b[N],x;

bool check(int x){
	ll cnt=0;
	for(int i=0;i<n;i++){
		if(a[i]>x){
			cnt+= ceil((double)(a[i]-x)/b[i]); //攻击力下降到x的总次数,向上取整 
			if(cnt>m)	return false;		
		}
	}
	return true;
}

ll sum(int a,int cnt,int b){ //a:首项,cnt:项数, b:差,d:末项 
	int d=a-b*(cnt-1);
	ll res=(ll)(a+d)*cnt>>1; //记得显示转换
	return res;
}

int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>a[i]>>b[i];
	}
	int left=0,right=1e6;
	while(left<=right){
		int mid=left+((right-left)>>1);
		if(check(mid)){
			right=mid-1;
			x=mid;
		}
		else	left=mid+1;
	}
	//x=left; //最后赋值或者在if里面赋值都一样
	ll res=0,cnt=0;
	for(int i=0;i<n;i++){
		if(a[i]>x){
			ll now=ceil((double)(a[i]-x)/b[i]);
			cnt+=now;
			res+=sum(a[i],now,b[i]); //等差数列求和算攻击力和 
		}
	}
	res+=(ll)(m-cnt)*x;//显示转换
	cout<<res;
	return 0;
} 
### 蓝桥杯单片机省 DS1302 示例代码及相关设计 在蓝桥杯单片机比中,DS1302作为一款常用的时钟芯片被广泛应用于题目中。以下是针对DS1302的相关资料和示例代码。 #### 1. DS1302 基本功能概述 DS1302 是一种低功耗实时时钟 (RTC) 芯片,具有12个寄存器,其中7个用于存储时间日期信息[^2]。这些寄存器采用二进制编码十进制 (BCD) 格式来保存数据。因此,在读取或写入数据时需要注意 BCD 编码的转换逻辑。 #### 2. DS1302 驱动头文件定义 以下是从第八届“蓝桥杯”单片机省中的 `ds1302.h` 文件提取的功能声明: ```c #ifndef __DS1302_H #define __DS1302_H void Write_Ds1302(unsigned char temp); void Write_Ds1302_Byte(unsigned char address, unsigned char dat); unsigned char Read_Ds1302_Byte(unsigned char address); #endif ``` 上述代码提供了三个主要函数接口: - **Write_Ds1302**: 向 DS1302 发送命令字节。 - **Write_Ds1302_Byte**: 将指定的数据写入到特定地址的寄存器中。 - **Read_Ds1302_Byte**: 从指定地址的寄存器中读取数据[^4]。 #### 3. DS1302 初始化与操作示例代码 下面是一段完整的初始化和操作 DS1302 的 C 语言代码示例: ```c #include "ds1302.h" // 定义 DS1302 寄存器地址 #define SECOND_ADDR 0x80 // 秒地址 #define MINUTE_ADDR 0x82 // 分地址 #define HOUR_ADDR 0x84 // 时地址 #define DAY_ADDR 0x86 // 日地址 #define MONTH_ADDR 0x88 // 月地址 #define WEEKDAY_ADDR 0x8A // 星期地址 #define YEAR_ADDR 0x8C // 年地址 // 写入秒数至 DS1302 void SetSecond(unsigned char second) { Write_Ds1302_Byte(SECOND_ADDR, DecToBcd(second)); } // 设置当前时间为 DS1302 void SetTime(unsigned char hour, unsigned char minute, unsigned char second) { Write_Ds1302_Byte(HOUR_ADDR, DecToBcd(hour)); // 写小时 Write_Ds1302_Byte(MINUTE_ADDR, DecToBcd(minute)); // 写分钟 Write_Ds1302_Byte(SECOND_ADDR, DecToBcd(second)); // 写秒钟 } // 获取当前时间 void GetTime(unsigned char *hour, unsigned char *minute, unsigned char *second) { *second = BcdToDec(Read_Ds1302_Byte(SECOND_ADDR)); // 读秒并转为十进制 *minute = BcdToDec(Read_Ds1302_Byte(MINUTE_ADDR)); // 读分并转为十进制 *hour = BcdToDec(Read_Ds1302_Byte(HOUR_ADDR)); // 读时并转为十进制 } ``` 此代码实现了对 DS1302 时间设置和读取的核心功能,并通过辅助函数完成十进制与 BCD 码之间的相互转换[^3]。 #### 4. 关键注意事项 由于 DS1302 使用 BCD 编码方式存储数值,所以在实际应用过程中需特别注意十进制与 BCD 码间的转换处理。例如,当向某个寄存器写入值时应先将其转化为 BCD 形式;而从寄存器读取出的内容也需要还原成普通的十进制表示形式以便进一步计算或显示。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值