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;
}

解题思路
要找到砍树砍够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;
}

解题思路
只移走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;
}