C++:第十讲二分查找

Everyday English

Your optimal career is simply this: Share the real you with physical world through th e process of creative self-expression.
你的最佳职业很简单,就是这样:通过创造性自我表达的途径和世界分享真实的你。

前言

很多人对二分感到很苦恼,很困惑,可能是因为二分的边界很难掌握,也许是判断条件难写…

然而,很幸运,你找到了这篇文章,仔细看下去,这篇文章将带你学透二分!!!

二分模版

#include <iostream>
#include <vector>
using namespace std;
// 二分查找模板
int binarySearch(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size() - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;

        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}
int main() {
    // 示例用法
    vector<int> nums = {1, 2, 3, 4, 5};
    int target = 3;
    int result = binarySearch(nums, target);
    cout << "Target found at index: " << result << endl;
    return 0;
}

记住,如果你不用万能头文件的话,一定要记得加#include <vector>

二分套这个模板,肯定没错!(只要判断条件写对)亲测有效!!!
下面的题目更能证明这句话!

洛谷小课堂B3627 立方根

题目描述

给定正整数n,求n sqrt 3。答案向下取整。

输入格式

仅一行,一个正整数 n。

输出格式

仅一行,一个正整数,表示 3 sqrt n。向下取整输出。

样例 #1

样例输入 #1
27

样例输出 #1
3

 样例 #2

样例输入 #2

100000
样例输出 #2
46
样例 #3

样例输入 #3
1000000000000000

样例输出 #3
100000

思路点拨

1.求 的3次根(下取整),等价于找到 ,满足 。
2.这里 有单调性,故可以二分 。
3.找到最大符合条件的整数 即可。

4.二分上下界:l=0,r=10的5次方+1。

AC:

#include<bits/stdc++.h>
using namespace std;
long long x,l,r,mid;
int main(){
  cin >> x;
  l=0; r=100001;
  while (r-l>1){
    mid=(l+r)>>1;
    if (mid*mid*mid<=x) l=mid; else r=mid;
 }
  cout << l << endl;
  return 0;
}

分析:这题就是典型的二分查找入门题。

二分查找是什么

可能你听说过二分查找,二分查找和二分答案是不是一回事呢?答案是否定的。二分查找只是单纯的查找就可以了,简单的控制好边界条件。而二分答案也许稍复杂些。

二分查找也称折半查找,顾名思义,就是每次查找去掉不符合条件的一半区间,直到找到答案(整数二分)或者和答案十分接近(浮点二分)。

话不多说,到例题里去练练手。

洛谷小课堂P1102 A-B 数对

 题目背景

出题是一件痛苦的事情!

相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!

 题目描述

给出一串正整数数列以及一个正整数 C,要求计算出所有满足 A - B = C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个正整数 N,C。

第二行,N 个正整数,作为要求处理的那串数。

输出格式

一行,表示该串正整数中包含的满足 A - B = C 的数对的个数。

样例 #1

样例输入 #1
4 1
1 1 2 3

样例输出 #1
3

思路点拨

这一题将A-B=C转换成A-C=B,首先将A数组每个元素出现的次数统计起来,用map映射,最后将A数组每次减一个C,再将A数组扫一遍,将所有映射的次数和加起来就是答案。

AC:

#include<bits/stdc++.h>
using namespace std; 
using namespace std;
typedef long long LL;
    LL a[200001];
    map<LL,LL> m;
    int main() {
        int n;
        LL c;
        LL ans=0;
        cin >> n >> c;
        for(int i=1;i<=n;i++) {
            cin >> a[i];
            m[a[i]]++;
            a[i]-=c;    
        } 
        for(int i=1;i<=n;i++) ans+=m[a[i]];
        cout << ans << endl;
    return 0;
}

最后,我们再来看一个浮点二分:

洛谷小课堂P1163 银行贷款

银行贷款

题目描述

当一个人从银行贷款后,在一段时间内他(她)将不得不每月偿还固定的分期付款。这个问题要求计算出贷款者向银行支付的利率。假设利率按月累计。

输入格式

三个用空格隔开的正整数。

第一个整数表示贷款的原值 w=0,第二个整数表示每月支付的分期付款金额 w,第三个整数表示分期付款还清贷款所需的总月数 m。

输出格式

一个实数,表示该贷款的月利率(用百分数表示),四舍五入精确到 0.1%。

数据保证答案不超过 300.0%。

样例 #1

样例输入 #1
1000 100 12
样例输出 #1
2.9

思路点拨

分析:对于月利率,大几率是小数,那么,我们就需要浮点二分。

月利率的范围可以放大些,比如,0~500,然后从这个范围里查,直到和答案极度相近,终止。 最后的l或r,精确位数之后就是正确✔答案啦!

AC:

#include<bits/stdc++.h>
using namespace std; 
double n,m,k,l,r;
bool pd(double x){ 
	return (pow(1.0/(1.0+x),k)>=1-n/m*x);
}
int main(){
	cin>>n>>m>>k;
	l=0;r=10;
	while(r-l>=0.0001){
		double mid=(l+r)/2;
		if(pd(mid))r=mid;
		else l=mid;
	}
	cout<<fixed<<setprecision(1)<<l*100;
    return 0;
}

至此,相信你已经对二分查找有一个更加清晰的认识了。

课后再来几个练习题吧(洛谷里的):

1、眼红的Medusa

2、进击的奶牛

3、切绳子

以下题解是上面的题目的答案(一一对应)

1.AC:

#include<bits/stdc++.h>
using namespace std;
int n,m;
map<int,bool> v;
int a[101000];
int b[101000];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		cin>>b[i];
		v[b[i]]=true;
	}
	for(int i=1;i<=n;i++){
		if(v[a[i]]){
			cout<<a[i]<<" ";
		}
	}
	return 0;
}

2.AC:

#include<bits/stdc++.h>
using namespace std;
int const N=1e5+5;
int a[N],n,m,l,r,mid;
bool C(int x){
	int sum=1;
	int last=1;
	for(int i=2;i<=n;i++){
		if(a[i]-a[last]>=x){
			sum++;
			last=i;
		}
	}
	return sum>=m;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	sort(a+1,a+n+1);
	l=0,r=1e9+5;
	while(r-l>1){
		mid=(l+r)/2;
		if(C(mid)){
			l=mid;
		}
		else{
			r=mid;
		}
	}
	cout<<l<<endl;
	return 0;
}

3.AC:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
double a[N],l,r,mid;
int n,k;
int C(double x){
	int ans=0;
	for(int i=1;i<=n;i++){
		ans+=(int)(a[i]/x);
	}
	return ans>=k;
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	l=0;r=1e9;
	for(int t=0;t<100;t++){
		mid=(r+l)*0.5;
		if(C(mid)){
			l=mid;
		}
		else{
			r=mid;
		}
	}
	printf("%.2lf",floor(l*100)/100);
	return 0;
}

结尾

本篇文章共3890字,如果你能支持一下我,我十分感谢!!!

如果你喜欢或想了解一下其他的算法,可以看看以下这些:

前缀和与差分:

C++:第九讲前缀和与差分-CSDN博客

贪心:

C++:第八讲贪心算法1-CSDN博客

排序:

C++:第七讲冒泡排序-CSDN博客

函数:

C++第6讲max和min函数_c++ min函数-CSDN博客

C++第五讲函数初步-CSDN博客

for循环&数组:

C++第四讲for循环及数组-CSDN博客

if语句&else语句及运算:

C++第三讲:C++中的逻辑运算符及if else语句-CSDN博客

基础:

C++第二讲输入与输出-CSDN博客

C++第一讲认识C++编译器-CSDN博客

最后认识一下,我是爱编程的喷火龙廖,我们有缘再见!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喷火龙廖

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值