二分练习题

 基础知识

算法基础之二分及二分答案-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/gege_0606/article/details/139041886?spm=1001.2014.3001.5502

题目一 

789. 数的范围 - AcWing题库icon-default.png?t=N7T8https://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)icon-default.png?t=N7T8https://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)icon-default.png?t=N7T8https://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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值