基础知识
算法基础之二分及二分答案-CSDN博客https://blog.csdn.net/gege_0606/article/details/139041886?spm=1001.2014.3001.5502
题目一
789. 数的范围 - AcWing题库https://www.acwing.com/problem/content/791/
分析
这道题目是二分两个模板的使用,详细解释请点击 算法基础之二分及二分答案-CSDN博客
代码示例:
#include <iostream>
using namespace std;
const int N = 1e4 + 10;
int a[N];
int main() {
int n, q;
cin >> n >> q;
for (int i = 0; i < n; i++) cin >> a[i];
while (q--) {
int k;
cin >> k;
// 查看元素的起始位置,右半部分都 >= k
int l = 0, r = n - 1;
while (l < r) {
int mid = l + ((r - l) >> 1);
if (a[mid] >= k) r = mid;
else l = mid + 1;
}
if (a[l] != k) {
cout << "-1 -1" << endl;
} else {
cout << l << ' ';
// 查找终止位置
r = n - 1;
while (l < r) {
int mid = l + ((r - l + 1) >> 1);
if (a[mid] <= k) l = mid;
else r = mid - 1;
}
cout << l << endl;
}
}
return 0;
}
题目二
P1678 烦恼的高考志愿 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1678
分析
对于这道题目,如果直接暴力(双重for循环),会超时;
但是当我们只用一层for循环加二分时间复杂度O(n*logm);
既然用二分,那么二分什么呢?
我们可以枚举储存每个学生分数的下标,然后根据通过mid访问数组a中的元素和b[i](学生的估计分数)来作比较,每次都更新ans(ans在每次循环当中表示第i个学生最小的不满意度)
- 如果a[mid]<b[i]说明枚举范围里面的数太小了需要让l = mid;
- 否则就说明枚举范围里面的分数太高了,需要减少,er= mid -1;
当退出循环的时候,此时l = r,别忘了还需要计算a[l]和b[i]的差值
相应AC代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5+10;
ll a[N], b[N];
ll m, n;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> m >> n;
for (int i = 1; i <= m; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
sort(a + 1, a + m + 1); // 对学校排序
ll totalMinDistance = 0;
for (int i = 1; i <= n; i++) { // 对每个学生
int l = 1, r = m;
ll ans = LLONG_MAX;
while (l < r) {
int mid = l + (r - l + 1) / 2;
ans = min(ans, abs(b[i] - a[mid]));
if (a[mid] < b[i]) {
l = mid ;
} else {
r = mid - 1;
}
}
// 循环结束后检查最后一个位置
ans = min(ans, abs(b[i] - a[l]));
totalMinDistance += ans;
}
cout << totalMinDistance << endl;
return 0;
}
题目三
P1102 A-B 数对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1102
分析
阅读题目后我的第一个想法是双指针,由于今天要学会的算法是二分,在这里就用STL里面的函数low_bound和upper_bound来解决该问题。
题目是给出一个序列,要我们找到A-B=C的数对,那么显然对于每一个数A,我们需要找到一个数B,让它们相差等于C,那么就是让我们找到找到A-C= B
low_bound就能找出在序列里面不小于B的元素,upper_bound就是找到在序列里面不大于B的元素,那么upper_bound - low_bound 的值就是满足条件的个数。
循环所有的元素,把每个a[i]对应的满足条件的B的个数累计即可。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5+10;
ll a[N];
ll c, n;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>c;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
ll ans = 0;
for(int i=1;i<=n;i++){
ans+=((upper_bound(a+1,a+n+1,a[i]-c)-a)-(lower_bound(a+1,a+n+1,a[i]-c)-a));
}
cout<<ans<<'\n';
return 0;
}