二分专题(二分查找/二分答案)

=====二分查找=====

=====二分答案=====

技巧总结:“最多”--->思考答案是否具有二段性,使用二分答案的好处就在于“多一个条件”(多固定一个条件)。

Leetcode 2576. 求出最多标记下标

 思考过程:

方法一:贪心+二分答案 

class Solution {
public:
    // 二分答案,多增加一个条件
    int maxNumOfMarkedIndices(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        auto check = [&](int mid) ->bool{   // mid对下标
            for(int j = 0; j < mid; j ++)
                if(nums[j] * 2 > nums[n - mid + j])   return false;
            return true;
        };
        int l = 0, r = n / 2;
        while(l < r){
            int mid = (l + r + 1) >> 1;
            if(check(mid)) l = mid;
            else           r = mid - 1;
        }
        return l * 2;
    }
};

方法二:贪心+双指针

class Solution {
public:
    int maxNumOfMarkedIndices(vector<int>& nums) {
        ranges::sort(nums);
        int i = 0, n = nums.size();
        for (int j = (n + 1) / 2; j < n; j++) {
            if (nums[i] * 2 <= nums[j]) { // 找到一个匹配
                i++;
            }
        }
        return i * 2;
    }
};

Leetcode 2398. 预算内的最多机器人数目

        读题中,注意连续运行的含义是:下标是连续的才行。

方法一:RMQ+前缀和+二分答案

显然这个答案具有“二段性”,那么可以二分,外循环时间复杂度变成logn级别。

看了下数据范围,大概是要求nlogn及以内才能过掉。然后二分枚举的每一个长度为k的区间中,需要O(n)来枚举每一个区间,所以查询区间最大值max和区间的sum只能用O(1)的时间,所以结合静态数组的特性,只能使用RMQ和前缀和来完成查询。

class Solution {
public:
    long long pre[50010] = {0};       // 前缀和数组
    long long f[50010][17];    // ST表
    int n;
    void init(vector<int>& w){
        for(int j = 0; j < 17; j ++)
            for(int i = 0; i + (1 << j) - 1 < n; i ++)
                if(!j)  f[i][j] = w[i];
                else    f[i][j] = max(f[i][j - 1], f[i + (1 << j)][j - 1]);
    }

    int query(int l, int r){    // 返回某个区间的最大值
        int len = r - l + 1;
        int k = log(len) / log(2);  // 自动下取整
        return max(f[l][k], f[r - (1 << k) + 1][k]);
    }

    // 二分答案
    int maximumRobots(vector<int>& chargeTimes, vector<int>& runningCosts, long long budget) {
        n = chargeTimes.size();
        int l = 0, r = n;       // 搜索的区间别搞错了, 最大是可以连续运行n个机器人的
        init(chargeTimes);     // 初始化st表
        for(int i = 0; i < n; i ++)
            pre[i + 1] = pre[i] + runningCosts[i];
        auto check = [&](int k) -> bool{  // 看这个k能不能行得通(得用O(n)的时间)
            for(int i = 0; i + k - 1 < n; i ++){
                int l = i, r = i + k - 1;
                if ((query(l, r) + k * (pre[r + 1] - pre[l])) <= budget)
                    return true;
            }return false;
        };
        while (l < r){
            int mid = (l + r + 1) >> 1;
            if(check(mid))  l = mid;
            else            r = mid - 1;
        }
        return l;
    }
};

方法二:单调队列/滑动窗口

见博客滑动窗口专题-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/zjjaibc/article/details/140856804?spm=1001.2014.3001.5501

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Ocean__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值