二分查找模版及二分答案例题

1.案例力扣查找元素第一个和最后一个位置

1.暴力

没有借助数组特性

2.二分查找


时间复杂度

n/2/2/2/2

x= logn

为了防⽌溢出,求中点时可以下⾯的⽅式:

• mid = l + (r - l) / 2

代码

cpp 复制代码
//求左端点
int left = 1; int right = n;
while (left < right)
{
	int mid = (left + right) / 2;
	if (a[mid] >= t)
	{
		right = mid;
	}
	else
	{
		left = mid + 1;
	}
}
//判断是否合法
if (a[left] == t)
{
	retleft = left;
}
else
{
	retleft = -1;
}

//求右端点
left = 1; right = n;
while (left < right)
{
	int mid = (left + right + 1) / 2;
	if (a[mid] <= t)
	{
		left = mid;
	}
	else
	{
		right = mid - 1;
	}
}

//判断是否合法
if (a[left] == t)
{
	retright = left;
}
else
{
	retright = -1;
}

1.2牛可乐和魔法封印



解题思路

已知边界值,要求出数组中在这两个数字中的个数

要求数组中大于等于xi和小于等于yi的数,具有二段性,可以用二分算法来分别求出左端点和右端点。
代码

cpp 复制代码
#include<iostream>
using namespace std;
typedef long long int LL;
const int N = 1e6;
int a[N];
int n,q;

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	cin >> q;

	while (q--)
	{
		int x, y;
		cin >> x >> y;
		int retleft = 0; int retright = 0;
		//求左端点
		int left = 1; int right = n;
		while (left < right)
		{
			int mid = (left + right) / 2;
			if (a[mid] >= x)
			{
				right = mid;
			}
			else
			{
				left = mid + 1;
			}
		}
		retleft = left;
		if (a[left] < x)
		{
			retleft = 0;
		}
		

		//求右端点
		left = 1; right = n;
		while (left < right)
		{
			int mid = (left + right + 1) / 2;
			if (a[mid] <= y)
			{
				left = mid;
			}
			else
			{
				right = mid - 1;
			}
		}
		retright = right;
		if (a[right] > y)
		{
			retright = 0;
		}
		
		if (retleft == 0 || retright == 0) {
			cout << 0 << endl;
		}
		else {
			cout << retright - retleft + 1 << endl;
		}
	}
	return 0;
}

1.3P1102 A-B 数对


解题思路

枚举A,B=A-C,求出A-C的值,利用二分,找到该值在数组中的左右端点。

代码

cpp 复制代码
#include<iostream>
#include <algorithm>
using namespace std;
typedef long long int LL;
const int N = 2e5 + 10;
LL a[N];
LL n,c;

int main()
{
	cin >> n >> c;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	sort(a + 1, a + 1 + n);

	//B = A - C  B < A,所以A为数组第一位时不可能有B元素
	LL ret = 0;
	for (int i = 2; i <= n; i++)
	{
		LL tmp = a[i] - c;
		ret += upper_bound(a + 1, a + 1 + n, tmp) - lower_bound(a + 1, a + 1 + n, tmp);
	}

	cout << ret << endl;

	
}

1.4烦恼的⾼考志愿


解题思路

求出距离每个同学最近的分数即可,所以可以利用二分求出大于等于分数的最小值,

设a[I]为学校分数,若求出的大于等于分数的最小值为数组的第一个元素,那么小于该分数的最小值为a[0-1],不合法所以进行处理。(左护法)

代码

cpp 复制代码
#include<iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
long long int  a[N], b[N];
int n, m;

int main()
{
	cin >> n >> m;
	a[0] = -1e7;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	sort(a + 1, a + 1 + n);

	long long int ret = 0;

	for (int i = 1; i <= m; i++)
	{
		cin >> b[i];
		
		int left = 1; int right = n;
		while (left < right)
		{
			int mid = (left + right) / 2;
			if (a[mid] >= b[i])
			{
				right = mid;
			}
			else
			{
				left = mid + 1;
			}

		}
		ret += min(abs(a[left] - b[i]), abs(a[left - 1] - b[i]));
	}

	cout << ret << endl;
}

二分答案

1.1P2440 木材加工


解题思路

找到可以满足切割段数的每段木材的最大值。

当每段木材的长度越长,可切割段数越小

当每段木材的长度越小,可切割段数越大

所以求的是右端值

代码

cpp 复制代码
#include<iostream>
using namespace std;
typedef long long int LL;
const int N = 1e5 + 10;
int a[N];
int n, k;

int cacl(int mid)
{
	LL ret = 0;
	for (int i = 1; i <= n; i++)
	{
	    //计算切割段数
		ret += a[i] / mid;
	}
	return ret;
}


int main()
{
	cin >> n >> k;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}

	int l = 0; int r = 1e8;
	while (l < r)
	{
		int mid = (l + r + 1) / 2;
		if (cacl(mid)>= k)
		{
			l = mid;
		}
		else
		{
			r = mid - 1;
		}
	}

	//判断是否合法
	if (l < 1)
	{
		l = 0;
	}

	cout << l << endl;
	return 0;
}

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


解题思路

要找到砍树砍够M米并且高度最高的值

如果砍树高度设置越高,能得到的木材越少

如果砍树高度设置越低,能得到的木材越多

所以求的是右端值

代码

cpp 复制代码
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
typedef long long int LL;
int n;
LL m;
int a[N];

LL calc(LL mid)
{
	LL ret = 0;
	for (int i = 1; i <= n; i++)
	{
		if (a[i] < mid)
		{
			ret += 0;
		}
		else
		{
			ret += a[i] - mid;
		}
	}
	return ret;
}

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}

	LL l = 0; LL r = 4e5;
	while (l < r)
	{
		LL mid = (l + r + 1)/2;
		if (calc(mid) >= m)
		{
			l = mid;
		}
		else
		{
			r = mid - 1;
		}
	}
	
	cout << l << endl;
	return 0;
}

1.3P2678 [NOIP 2015 提高组] 跳石头


解题思路

只移走M快石头的前期下,使选手的最小跳跃值最大

如果最小跳跃值越小,可移走的石头越少

如果最小跳跃值越大,可移走的石头越多

求右端值

代码

cpp 复制代码
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 5e4 + 10;
LL l, n, m;
LL a[N];
// 当最短跳跃距离为 x 时,移⾛的岩⽯数⽬ 
LL calc(LL x)
{
 LL ret = 0;
 for(int i = 0; i <= n; i++)
 {
 int j = i + 1;
 while(j <= n && a[j] - a[i] < x) j++;
 ret += j - i - 1;
 i = j - 1;
 }
 return ret;
}
int main()
{
 cin >> l >> n >> m;
 for(int i = 1; i <= n; i++) cin >> a[i];
 a[n + 1] = l;
 n++;
 LL left = 1, right = l;
 while(left < right)
 {
 LL mid = (left + right + 1) / 2;
 if(calc(mid) <= m) left = mid;
 else right = mid - 1;
 }
 cout << left << endl;
 return 0;
}
相关推荐
CoderCodingNo7 小时前
【NOIP】2011真题解析 luogu-P1003 铺地毯 | GESP三、四级以上可练习
算法
iFlyCai7 小时前
C语言中的指针
c语言·数据结构·算法
查古穆7 小时前
栈-有效的括号
java·数据结构·算法
再一次等风来7 小时前
近场声全息(NAH)仿真实现:从阵列实值信号到波数域重建
算法·matlab·信号处理·近场声全息·nah
汀、人工智能7 小时前
16 - 高级特性
数据结构·算法·数据库架构·图论·16 - 高级特性
你撅嘴真丑7 小时前
[蓝桥杯 2025 省 B] 生产车间 与 装修报价
职场和发展·蓝桥杯
大熊背7 小时前
利用ISP离线模式进行分块LSC校正的方法
人工智能·算法·机器学习
XWalnut8 小时前
LeetCode刷题 day4
算法·leetcode·职场和发展
蒸汽求职8 小时前
机器人软件工程(Robotics SDE):特斯拉Optimus落地引发的嵌入式C++与感知算法人才抢夺战
大数据·c++·算法·职场和发展·机器人·求职招聘·ai-native
Tanecious.8 小时前
蓝桥杯备赛:Day4-P9749 公路
c++·蓝桥杯