二分前提条件
-
数组的有序的数组
-
数组中无重复元素,否则查找的元素下标可以不算唯一的
二分答案
- 二分答案时需要满足答案的有界性
- 二分答案的思路:首先判断这个解是否为可行解,然后我们”认为“这个可行解的最优解,然后以这个可行解为标准去左(右)区间寻找最优解
时间复杂度
O(logN)
查找一个数的下标
左闭右闭区间 [l, r]
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
while (l <= r) { // 注意是 <=
int mid = l + (r - l) / 2; // 防止溢出,等价于 (l + r) / 2
if (nums[mid] > target)
r = mid - 1; // 注意
else if (nums[mid] < target)
l = mid + 1; // 注意
else return mid;
}
return -1;
}
};
左闭右开区间 [l, r)
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size(); // 注意 r 的初始化
while (l < r) {
int mid = l + ((r - l ) >> 1); // 注意 >> 1 要加括号
if (nums[mid] > target)
r = mid; // 注意
else if (nums[mid] < target)
l = mid + 1; // 注意
else
return mid;
}
return -1;
}
};
查找第一次出现的位置
int ans = -1;
int l = 0, r = 7;
while (l <= r) // 注意 l == r 时还需要查询
{
int mid = l + (r - l >> 1);
if (arr[mid] > num)
r = mid - 1;
else if (arr[mid] < num)
l = mid + 1;
else
{
ans = mid;
r = mid - 1;
}
}
查找最后一次出现的位置
int ans = -1;
int l = 0, r = 7;
while (l <= r) // 注意 l == r 时还需要查询
{
int mid = l + (r - l >> 1);
if (arr[mid] > num)
r = mid - 1;
else if (arr[mid] < num)
l = mid + 1;
else
{
ans = mid;
l = mid + 1;
}
}
题目
P1102 A-B数对
题目描述
给出一串正整数数列以及一个正整数 C,要求计算出所有满足 A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个正整数 N,C。
第二行,N 个正整数,作为要求处理的那串数。
输出格式
一行,表示该串正整数中包含的满足 A−B=C 的数对的个数。
样例 #1
样例输入 #1
4 1
1 1 2 3
样例输出 #1
3
提示
对于 75% 的数据,1≤N≤2000。
对于 100% 的数据,1≤N≤2×105,0≤ai<230,1≤C<230。
2017/4/29 新添数据两组
思路
A = B + C, 二分查找符合条件的A的个数,当然也可以直接用map
解决这个问题
const int N = 2e5 + 2;
int arr[N];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, c;
cin >> n >> c;
for (int i = 0; i < n; ++i)
cin >> arr[i];
sort(arr, arr + n);
int ans = 0;
for (int i = 0; i < n; ++i)
{
// 查找第一次和最后一次出现的位置 ans += ind2 - ind 1 + 1
int num = c + arr[i];
int ind1 = -1, ind2 = -1;
int l = 0, r = n - 1;
while (l <= r)
{
int mid = l + (r - l >> 1);
if (arr[mid] > num)
r = mid - 1;
else if (arr[mid] < num)
l = mid + 1;
else
{
ind1 = mid;
r = mid - 1;
}
}
l = 0, r = n - 1;
while (l <= r)
{
int mid = l + (r - l >> 1);
if (arr[mid] > num)
r = mid - 1;
else if (arr[mid] < num)
l = mid + 1;
else
{
ind2 = mid;
l = mid + 1;
}
}
if (ind1 != -1)
ans += ind2 - ind1 + 1;
}
cout << ans << endl;
return 0;
}
也可以直接用STL
中的二分函数
const int N = 2e5 + 2;
int arr[N];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, c;
cin >> n >> c;
for (int i = 0; i < n; ++i)
{
cin >> arr[i];
}
sort(arr, arr + n);
int ans = 0;
for (int i = 0; i < n; ++i)
{
ans += (upper_bound(arr, arr + n, arr[i] + c) - lower_bound(arr, arr + n, arr[i] + c));
}
cout << ans << endl;
return 0;
}
P1873 砍树
题目描述
伐木工人 Mirko 需要砍 M 米长的木材。对 Mirko 来说这是很简单的工作,因为他有一个漂亮的新伐木机,可以如野火一般砍伐森林。不过,Mirko 只被允许砍伐一排树。
Mirko 的伐木机工作流程如下:Mirko 设置一个高度参数 H(米),伐木机升起一个巨大的锯片到高度 H,并锯掉所有树比 H 高的部分(当然,树木不高于 H 米的部分保持不变)。Mirko 就得到树木被锯下的部分。例如,如果一排树的高度分别为 20,15,10 和 17,Mirko 把锯片升到 15 米的高度,切割后树木剩下的高度将是 15,15,10 和 15,而 Mirko 将从第 1 棵树得到 5 米,从第 4 棵树得到 2 米,共得到 7 米木材。
Mirko 非常关注生态保护,所以他不会砍掉过多的木材。这也是他尽可能高地设定伐木机锯片的原因。请帮助 Mirko 找到伐木机锯片的最大的整数高度 H,使得他能得到的木材至少为 M 米。换句话说,如果再升高 1 米,他将得不到 M 米木材。
输入格式
第 1 行 2 个整数 N 和 M,N 表示树木的数量,M 表示需要的木材总长度。
第 2 行 N 个整数表示每棵树的高度。
输出格式
1 个整数,表示锯片的最高高度。
样例 #1
样例输入 #1
4 7
20 15 10 17
样例输出 #1
15
样例 #2
样例输入 #2
5 20
4 42 40 26 46
样例输出 #2
36
提示
对于 100% 的测试数据,1≤N≤106,1≤M≤2×109,树的高度 ≤4×105,所有树的高度总和 >M。
思路
二分答案,即对最大高度4e5进行二分,对每一次二分得到的高度mid
进行check
,然后缩小区间,直到结束
int n, m;
bool check(int mid, vector<int> arr)
{
int sum = 0;
for (auto c : arr)
{
if (c > mid)
sum += c - mid;
else
break;
}
return sum >= m;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
vector<int> arr(n);
for (int i = 0; i < n; ++i)
{
cin >> arr[i];
}
sort(arr.begin(), arr.end(),
[](int a, int b)
{ return a > b; });
int l = 0, r = 4e5;
int ans = 0;
while (l <= r)
{
int mid = l + (r - l >> 1);
if (check(mid, arr))
{
ans = mid;
l = mid + 1;
}
else
r = mid - 1;
}
cout << ans << endl;
return 0;
}
P2440 木材加工
题目背景
要保护环境
题目描述
木材厂有 n 根原木,现在想把这些木头切割成 k 段长度均为 l 的小段木头(木头有可能有剩余)。
当然,我们希望得到的小段木头越长越好,请求出 l 的最大值。
木头长度的单位是 cm,原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。
例如有两根原木长度分别为 11 和 21,要求切割成等长的 6 段,很明显能切割出来的小段木头长度最长为 5。
输入格式
第一行是两个正整数 n,k,分别表示原木的数量,需要得到的小段的数量。
接下来 n 行,每行一个正整数 Li,表示一根原木的长度。
输出格式
仅一行,即 l 的最大值。
如果连 1cm 长的小段都切不出来,输出 0
。
样例 #1
样例输入 #1
3 7
232
124
456
样例输出 #1
114
提示
数据规模与约定
对于 100% 的数据,有 1≤n≤105,1≤k≤108,1≤Li≤108(i∈[1,n])。
思路
二分答案,即对最长的木头长度二分,对每一次二分得到的mid
进行check
,如果段数够就往右搜索,因为段数k
是一定的,那么每段的长度mid
越大,剩下的就越小
int n, k;
bool check(int mid, vector<int> arr)
{
int s = 0;
for (int c : arr)
{
s += c / mid;
}
return s >= k;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
vector<int> arr(n);
for (int i = 0; i < n; ++i)
{
cin >> arr[i];
}
sort(all(arr));
int l = 1, r = arr.back();
int ans = 0;
while (l <= r)
{
int mid = l + (r - l >> 1);
if (check(mid, arr))
{
l = mid + 1; // 一定是往右搜索,因为段数是一定的,mid越大,剩下的就越小
ans = max(ans, mid);
}
else
r = mid - 1;
}
cout << ans << endl;
return 0;
}
P2678 跳石头
题目背景
NOIP2015 Day2T1
题目描述
一年一度的“跳石头”比赛又要开始了!
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。
输入格式
第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L≥1 且 N≥M≥0。
接下来 N 行,每行一个整数,第 i 行的整数 Di(0<Di<L), 表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
输出格式
一个整数,即最短跳跃距离的最大值。
样例 #1
样例输入 #1
25 5 2
2
11
14
17
21
样例输出 #1
4
提示
输入输出样例 1 说明
将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。
数据规模与约定
对于 20%的数据,0≤M≤N≤10。
对于 50% 的数据,0≤M≤N≤100。
对于 100% 的数据,0≤M≤N≤50000,1≤L≤109。
思路
二分答案,根据最多可以搬走的石头数量对每个mid
进行check
,然后向右搜索
int L, n, m;
bool check(int mid, vector<int> arr)
{
int cnt = 0; // 为使mid成为可行解,需要搬走的石头数量
for (int i = 1; i <= n + 1; i++)
{
if (arr[i] - arr[i - 1] < mid)
cnt++, arr[i] = arr[i - 1];
}
if (cnt <= m)
return true;
return false;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> L >> n >> m;
vector<int> arr(n + 2);
for (int i = 1; i <= n; ++i)
{
cin >> arr[i];
}
arr.back() = L;
int l = 0, r = L;
int ans = 0;
while (l <= r)
{
int mid = l + (r - l >> 1);
if (check(mid, arr))
l = mid + 1, ans = mid;
else
r = mid - 1;
}
cout << ans << endl;
return 0;
}
标签:二分,arr,le,int,样例,mid,搜索,答案,ans
From: https://blog.csdn.net/2303_80261633/article/details/142763321