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

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;
}
相关推荐
biubiuibiu2 小时前
选择适合的硬盘:固态与机械硬盘的对比与推荐
c++·算法
big_rabbit05022 小时前
[算法][力扣226]翻转一颗二叉树
数据结构·算法·leetcode
TracyCoder1232 小时前
LeetCode Hot100(65/100)——64. 最小路径和
算法·leetcode·职场和发展
z2014z2 小时前
Deflate 算法详解
网络·算法
条tiao条2 小时前
从 “Top-K 问题” 入门二叉堆:C 语言从零实现与经典应用
c语言·算法·深度优先
uesowys2 小时前
华为OD算法开发指导-数据结构-图
数据结构·算法·华为od
实心儿儿2 小时前
算法3:链表分割
数据结构·算法·链表
Tisfy2 小时前
LeetCode 1415.长度为 n 的开心字符串中字典序第 k 小的字符串:DFS构造 / 数学O(n)
数学·算法·leetcode·深度优先·字符串·dfs·模拟
FriendshipT2 小时前
算法部署知识点:TensorRT、Tensorflow、Flask、Docker、TFLite
算法·docker·flask·tensorflow