【蓝桥杯AcWing】专题———二分(重点)

优雅的二分可以使复杂度达到O(n log n)

二分查找——最初的模样

首先保证的是,二分查找的序列是升序的。

查找小于等于temp的第一个元素位置

 void binary_lower(int temp){	//小于等于temp的第一个元素 
 	int l = 0, r = n - 1;
 	while(l <= r){
 		int middle = (l + r) >> 1;
 		if(a[middle] >= temp){
 			r = middle - 1;
 		}else{
 			l = middle + 1;
 		}
 	}
 	printf("%d\n", l);
 }

查找大于temp的第一个元素位置

 void binary_upper(int temp){	//大于temp的第一个元素 
 	int l = 0, r = n - 1;
 	while(l <= r){
 		int middle = (l + r) >> 1;
 		if(a[middle] > temp){
 			r = middle - 1;
 		}else{
 			l = middle + 1;
 		}
 	}
 	printf("%d\n", l);
 }
  • 不难发现,两段代码的差别仅在一个运算符符号,看看查找的结果吧
 int main(){
 	scanf("%d", &n);
 	for(int i = 0; i < n; ++i){
 		scanf("%d", &a[i]);
 	}
 	while(1){
 		int t;
 		scanf("%d", &t);
 		binary_lower(t);
 		//printf("%d\n", lower_bound(a, a+n, t) - a);
		binary_upper(t);	
		//printf("%d\n", upper_bound(a, a+n, t) - a);
 	}
 	return 0;
 }

如输入样例:
6
1 2 2 3 3 4
3
输出:
3
5

lower_bound()和upper_bound()

  • 正如上面代码注释的部分:
  • C++提供的lower_bound()即查找小于等于temp的第一个元素位置,upper_bound()即查找大于temp的第一个元素位置。

·【AcWing】·数的范围

题目链接:·【acwing】·数的范围

  • 查找temp的起始位置(即小于等于temp的第一个元素位置()
  • 查找temp的终止位置(即大于temp的第一个元素位置 - 1)
  • 【注】:temp在序列中还必须存在,若不存在,需要额外处理一下

于是,实现代码只需要在原来的binary_lower()binary_upper()稍作改动

————————————完整代码————————————

  #include <cstdio>
 #include <algorithm>
 
 using namespace std;
 
 int n;
 int a[100005];
 int binary_lower(int temp){	
 	int l = 0, r = n - 1;
 	bool flag = false;
 	while(l <= r){
 		int middle = (l + r) >> 1;
 		if(a[middle] >= temp){
 			if(a[middle] == temp){	//特别判断
 				flag = true;
 			}
 			r = middle - 1;
 		}else{
 			l = middle + 1;
 		}
 	}
 	if(!flag){		//不存在,返回-1
 		return -1;
 	}else{
 		return l;
 	}
 	//printf("%d\n", l);
 }
 int binary_upper(int temp){	 
 	int l = 0, r = n - 1;
 	bool flag = false;
 	while(l <= r){
 		int middle = (l + r) >> 1;
 		if(a[middle] > temp){
 			r = middle - 1;
 		}else{
 			if(a[middle] == temp){
 				flag = true;
 			}
 			l = middle + 1;
 		}
 	}
 	if(!flag){		//不存在,返回-1
 		return -1;
 	}else{
 		return l - 1;	//返回l - 1
 	}
 	//printf("%d\n", l);
 }
 int main(){
 	int t; 
 	scanf("%d%d", &n, &t);
 	for(int i = 0; i < n; ++i){
 		scanf("%d", &a[i]);
 	}
 	while(t > 0){
 		int temp;
 		scanf("%d", &temp);
 		int ans = binary_lower(temp);
 		if(ans == -1){
 			printf("-1 -1\n");
 		}else{
 			printf("%d %d\n", ans, binary_upper(temp));
 		}
 		t--;
 	}
 	return 0;
 }

·【AcWing】·数的三次方根

题目链接:·【AcWing】·数的三次方根

  • 求解方程 x 3 = n x^3 = n x3=n
    移项得 f ( x ) = x 3 − n f(x) = x^3 - n f(x)=x3n
  • 于是可以不断二分 x 1 x_1 x1 x 2 x_2 x2,零点定理(高等数学)得:
    f ( x 1 ) ∗ f ( x 2 ) < 0 f(x_1) * f(x_2) < 0 f(x1)f(x2)<0,则存在 f ( x ) = 0 f(x) = 0 f(x)=0,其中x在 [ x 1 , x 2 ] [x_1, x_2] [x1,x2]
  • 题目要求x精确到小数点后六位,根据经验,二分100次足矣

————————————完整代码————————————

#include <cstdio>

using namespace std;

double n;
double solve(double x){
	return x * x * x - n;
}
int main(){
	scanf("%lf", &n);
	double l, r;
	if(n > 0){
		l = 0;
		r = 10000;
	}else{
		l = -10000;
		r = 0;
	}
	for(int i = 0; i < 100; ++i){
		double middle = (l + r) / 2;
		if(solve(l)*solve(middle) < 0){		//根在(l, middle)
			r = middle;
		}else{		//根在(middle, r)
			l = middle;
		}
	}
	printf("%lf\n", l);
	//printf("%lf %lf\n", l, r);
	return 0;
}

·【蓝桥】·分巧克力

题目链接:·【蓝桥】·分巧克力

  • 很容易想到是一道二分的题目,不妨temp最大就从10^5满足,需要二分多少次呢?
  • 2^20 > 10 ^ 6,二分20次足矣

————————————完整代码————————————

#include <cstdio>

using namespace std;

const int maxn = 1e5 + 5;
struct note{
	int a, b;
}s[maxn];

int n, k;

bool judge(int temp){
	int ans = 0;
	for(int i = 0; i < n; ++i){
		ans += (s[i].a / temp) * (s[i].b / temp);
	}
	return ans >= k;
}
void solve(){	// 开始二分
	int l = 0, r = maxn*2;
	for(int i = 0; i < 20; ++i){
		int middle = (l + r) >> 1;
		if(judge(middle)){
			l = middle;
		}else{
			r = middle;
		}
	}
	printf("%d\n", l);
	//printf("%d %d\n", l, r);
}
int main(){
	scanf("%d%d", &n, &k);
	for(int i = 0; i < n; ++i){
		scanf("%d%d", &s[i].a, &s[i].b);
	}
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值