二分查找

13 篇文章 0 订阅
9 篇文章 0 订阅

二分是一种十分高效的工具,使用二分可以节省大量的时间。我们首先通过一个小练习来初步熟悉一下二分(转自YCOJ):

请问最后将输出什么呢?

 

答案是4343(两个43)。

通过这些,相信读者已经对二分有了初步的认识。接下来做一些二分的习题:


查找

Description

给出一个有 n 个元素的数列 a 和两个整数 k 和 s,其中数列 a 的元素是按照升序排列的。

请你在数列中找出一个元素 x,使得的 x+k=s。

Input

输入第一行一个整数 n(1≤n≤10^5),表示数列中的元素个数。

接下来一行输入 n 个空格隔开的整数,表示输入的数列 a,保证是升序排列,并且 −10^9≤ai≤10^9。

接下来一行输入两个整数 k,s(−10^9≤k,s≤10^9)。

Output

如果能找到满足条件的数,输出"Yes",否者输出"No"。

输入k、s之后,就能够算出x的值,然后用二分查找的方式在a数组里查找x是否存在就可以了。代码如下:

#include<iostream>
using namespace std;

int main () {
  int n;
  cin >> n;
  int a [n];
  for(int i = 0; i < n; i++){
  	cin >> a [i];
  }
  int k, s;
  cin >> k >> s;
  
  int l = 0, r = n - 1, ans = 0, x = s - k;
  while(l < r){
  	int mid = (l + r) / 2;
  	if (a [mid] < x){
  	  l = mid + 1;
	}
	else{
	  r = mid;
	}
  }
  if(a[l] == x) ans = 1;//当二分结束后,l和r一定是相等的
  
  if(ans) cout << "Yes" << endl;
  else cout << "No" << endl;
  return 0;
}

两数之和

Description

给出一个有 n 个元素的数列 a 和一个整数 s,其中数列 a 的元素是按照升序排列的。

请你在数列中找出两个元素 x,y使得的 x+y=s。

Input

输入第一行一个整数 n(1≤n≤10^5),表示数列中的元素个数。

接下来一个输入 n 个空格隔开的整数,表示输入的数列 a,保证是升序排列,并且 −10^9≤ai≤10^9。

接下来一行输入一个整数 s(−10^9≤x,s≤10^9)。

Output

如果能找到满足条件的两个数,输出"Yes",否者输出"No"。

这题和前面那题差别不大,在二分外面套一个循环就可以了:

#include<iostream>
using namespace std;

int main () {
  long long n;
  cin >> n;
  int a [n];
  for(int i = 0; i < n; i++) cin >> a [i];
  int s;
  cin >> s;
  
  int ans = 0;
  for(int i = 0; i < n; i++){
  	int l = 0, r = n - 1, x = s - a [i];
  	while(l < r){
  	  int mid = (l + r) / 2;
  	  if (a [mid] < x) l = mid + 1;
  	  else r = mid;
  	  if (l != i && r != i && a [l] == x && a [r] == x){
  	  	ans = 1;
  	  	break;
	  }
	}
	if (ans) break;
  }
  
  if (ans == 1) cout << "Yes" << endl;
  else cout << "No" << endl;
  return 0;
}

报数游戏

Description

小信在和他的朋友们一起玩一个游戏。由于小信的机智,这个游戏由小信担任裁判。

首先,小信会给每个朋友一个唯一的编号。接下来的每一回合,小信会说一个数,编号不超过它的最大编号的人要报出自己的编号。如果没有人的编号比小信给出的数小,那么编号最小的人要报出自己的编号。每个人可以重复报号。

小信会按照一个列表顺次说出每个回合的数,他的朋友们想知道每回合该报出的编号应该是多少。你能帮帮他们吗?

Input

输入数据共 3 行。第一行有两个整数 n,m(1≤n≤10^5,1≤m≤10^5),分别表示参与游戏的小信朋友的个数,和游戏的回合数。

第二行 n 个整数 ai(1≤ai≤10^8),表示朋友们每个人的编号。对于 0≤i<j<n,都有 ai<aj,即他们的编号递增排列。

第三行 m 个整数 qi(1≤qi≤108),表示每回合小信给的数字。

Output

输出共一行 m 个整数,表示每回合报出的编号,每两个整数之间一个空格,最后一个数后面没有空格。

对于q数组的每一个元素,都在a数组里二分地查找一下小于它的最大的那个数,然后对于q数组中小于a数组最小值的数和大于a数组中最大值的数进行特殊判断就可以了:

#include<iostream>
using namespace std;

const int maxn = 100000 + 5;
int a[maxn], q[maxn];

int main (){
  int n, m;
  cin >> n >> m;

  for(int i=0; i<n; i++) cin >> a[i];
  for(int i=0; i<m; i++) cin >> q[i];
  
  bool flag = 0;
  for (int i = 0; i < m; i++){
  	int ans;
  	if (a[0] > q[i]) ans = a[0];
	else if (a[n - 1] < q[i]) ans = a[n - 1];
	else{
	  int l = 0, r = n - 1;
  	  while (l < r){
  	    int mid = (l + r) / 2;
  	    if (a[mid] <= q[i]) l = mid + 1;
  	    else r = mid;
	  }
	  ans = a[l - 1];
	}
	
	if (flag) cout << " ";
	flag = 1;
	cout << ans;
  }
  return 0;
}

由于题目规定了输入的数据是天然排序的,所以我们不需要对数组再进行排序来寻找最大值。


方程求解

Description

有一个如下的方程需要你帮忙计算一下。

xe^x=a

其中,e 为自然底数,a 为一个常数。

请你求出满足等式的 x 的值。

Input

输入一个整数 a(0≤a≤10^9)。

Output

输出一个浮点数,表示方程的解,四舍五入到小数点后四位。

自然对数e^x 可以使用数学库中函数exp(x) 来计算;可以通过 while (r - l > 1e-6 ) { } 的方法来控制精度。

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

int main(){
  int a;
  cin >> a;
  
  double l = 0, r = 18, mid, temp;
  while(r - l > 1e-6){
  	mid = (r + l) / 2;
  	temp = mid * exp(mid);
  	if(temp < a) l = mid;
  	else r = mid;
  }
  
  printf("%.4lf", mid);
  
  return 0;
}

这里用了C语言的printf来控制输出的位数。


割绳子

Description

现有 N 条绳子,它们的长度分别为 L1,L2,……,Ln,如果从它们中切割出 K 条长度相同的绳子,这 K 条绳子每条最长能有多长?

Input

输入共有两行,第一行包含两个正整数 N 和 K(1≤N≤1000,1≤K≤1000),用一个空格分隔;第二行包含 N 个数,依次表示 N 条绳子的长度,两数间用一个空格分隔。每条绳子的长度的小数不超过两位(1≤Li≤10000)。

Output

输出仅包含一个数,表示所得 K 条绳子的最大长度。答案四舍五入保留小数点后两位。

这题只需要找出绳子中的最大值,然后在0和最大值之间用二分查找就可以解了:

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

int main(){
  int n, k;
  cin >> n >> k;
  double line[n], maxn = -52697;
  for(int i = 0; i < n; i++){
  	cin >> line[i];
  	if(line[i] > maxn) maxn = line[i];
  }
  
  double l = 0, r = maxn, mid;
  while(r - l > 1e-4){
  	mid = (r + l) / 2;
  	
  	int temp = 0;
  	for(int i = 0; i < n; i++) temp+= floor(line[i] / mid);
  	
  	if(temp >= k) l = mid;
  	else r = mid;
  }
  
  printf("%.2lf", mid + 1e-6);//加上一个较小的值可以防止四舍五入出错
  
  return 0;
}

注意这里也需要用一个较小的数控制精度。这里用的是1e-4。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值