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

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烦恼的⾼考志愿


解题思路

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

设aI为学校分数,若求出的大于等于分数的最小值为数组的第一个元素,那么小于该分数的最小值为a0-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;
}
相关推荐
倒头就睡的小比特4 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼4 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光4 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark4 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her14 天前
并查集-听课笔记
笔记·算法·并查集
码流子4 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven4 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark4 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218614 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法