LeetCode/二分法综合

1. 寻找两个正序数组的中位数

2. 两数相除

3. 快速幂

4. 搜索旋转排序数组

5. 数组中的逆序对

6. 在排序数组中查找元素的第一个和最后一个位置

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        return {find(nums,target,true),find(nums,target,false)};
    }
    int find(vector<int>& nums, int target, bool minType) {
        int left = 0, right = nums.size() - 1;
        int ans = -1;//默认为-1
        while (left <= right) {//当搜索区间位于两指针之间
            int mid = left + (right - left) / 2;//二分查找
            if (nums[mid] == target) {
                ans = mid;
                if (minType) right = mid - 1;//找左边界(注意没写反)
                 else left = mid + 1;}//找右边界(注意没写反)
             else if (target < nums[mid]) right = mid - 1;//移动右指针到二分位置
             else left = mid + 1;//左指针移到二分位置
            
        }
        return ans;
    }
};

7. 阶乘函数后k个零

8. 找出第k小的数对距离

class Solution {
public:
    int smallestDistancePair(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());//排序
        int n = nums.size();
        int left = 0, right = nums.back() - nums.front();
        while (left <= right) {
            int mid = (left + right) / 2;
            int cnt = 0;//计算所有距离小于等于mid的数对数目cnt
            for (int j = 0; j < n; j++) {
            int i = lower_bound(nums.begin(), nums.begin() + j, nums[j] - mid) - nums.begin();
            cnt += j - i;
            }
            if (cnt >= k) right = mid - 1;
            else left = mid + 1;
        }
        return left;
    }
};

9. 爱吃香蕉的珂珂

class Solution {
public:
    int minEatingSpeed(vector<int>& piles, int h) {
        long sum =0;int max_ = 0;
        for(int x:piles){
            sum+=x;
            max_ = max(max_,x);}
        int min_ = ceil(sum*1.0/h);//向上取整
        while(min_<max_){
            int times = 0;
            int mid = (max_+min_)/2;
            for(int x:piles)
                times = times + ceil(x*1.0/mid);
            if(times==h) max_ = mid; //找左边界动右边
            else if(times<h) max_ = mid; 
            else if(times>h) min_ = mid+1;//找左边界
        }
        return min_;
    }
};

10. 使结果不超过阈值的最小除数

class Solution {
public:
    int smallestDivisor(vector<int>& nums, int threshold) {
        int min_ = 1; int max_ = *max_element(nums.begin(), nums.end());
        while(min_<max_){//使得尽可能小,取左边界
            int sum = 0;
            int mid = (min_+max_)/2;
            for(int &x:nums)
                sum+=ceil(x*1.0/mid);
            if(sum>threshold) min_ = mid + 1;//如果相等,也要使得尽可能除数变小,所以是降上界
            else max_ = mid;
        }
        return min_;
    }
};

11. 完成旅途的最少时间

class Solution {
public:
    long long minimumTime(vector<int>& time, int totalTrips) {
    long long min_ = 1; long long max_ = *min_element(time.begin(), time.end());
    max_ = max_ * totalTrips;
        while(min_<max_){
            long long sum = 0;
            long long mid = (min_+max_)/2;//当前总时间
            for(int &x:time)
                sum+=mid/x;;//每个车能进行的趟数求和
            if(sum>=totalTrips) max_ = mid;//要使得时间尽可能小,相等也要降
            else min_ = mid + 1;
        }
        return min_;
    }
};

12. 每个小孩最多能分到多少糖果

class Solution {
public:
    int maximumCandies(vector<int>& candies, long long k) {
        if(accumulate(candies.begin(),candies.end(),0ll)<k) return 0;
        long long min_ = 1; long long max_ = *max_element(candies.begin(), candies.end());
        while(min_<max_){
            long long sum = 0;
            long long mid = (min_+max_+1)/2;//每个孩子所拿糖果数,这里需要往上取
            //不加1,直接整除,默认是往下取
            for(int &x:candies)
                sum+=x/mid;;//可以分成多少满足条件的堆
            if(sum>=k) min_ = mid;//满足条件的情况下,
            //下一次糖果数往上取,但在该次要保证min_值始终满足条件,用于可能的输出
            else max_ = mid-1;
        }
        return min_;
    }
};

13. 准时到达的列车最小时速

class Solution {
public:
    int minSpeedOnTime(vector<int>& dist, double hour) {
        if(dist.size()-1>=hour) return -1;
        int min_ = 1; int max_ = INT_MAX-1;
        while(min_<max_){
            int mid = (max_+min_)/2;//最小化时速
            double sum = 0;
            for(int i=0;i<dist.size()-1;i++)
                sum+=ceil(dist[i]*1.0/mid);//当前用时和
            sum+=dist.back()*1.0/mid;
            if(sum<=hour) max_ = mid; //满足条件,找下界
            else min_ = mid+1;
        }
        return min_;
    }
};

14. 在 D 天内送达包裹的能力

class Solution {
public:
    int shipWithinDays(vector<int>& weights, int days) {
        int left = *max_element(weights.begin(), weights.end());
        int right = accumulate(weights.begin(),weights.end(),0);
        while(left<right){
            int mid = left + (right-left)/2;
            int cur = 1;//所用天数
            int ws = 0;//当前累积载重
            for(int i=0;i<weights.size();i++){
                if(ws+weights[i]>mid){
                    ws = 0;//清零换一天
                    cur++;
                }
                ws+=weights[i];
            }
            //
            if(cur<=days)  right = mid;//缩小到下边界
            else left = mid + 1;
        }
        return right;
    }
};

15. 分配给商店的最多商品的最小值

class Solution {
public:
    int minimizedMaximum(int n, vector<int>& quantities) {
        int left = 1; int right = *max_element(quantities.begin(),quantities.end());
        while(left<right){
            int sum = 0;//可以分配给商店的最小数量
            int mid = (left+right)/2;//当前分配的最多商品数
            for(int &x:quantities)
                sum+=ceil(x*1.0/mid);//下面需要最小化mid,即相等时也要使其减少,找到下界
            if(sum>n) left = mid+1;
            else right = mid;
        }
        return left;
    }
};

16. 袋子里最少数目的球

class Solution {
public:
    int minimumSize(vector<int>& nums, int maxOperations) {
        int left = 1;
        int right = *max_element(nums.begin(),nums.end());//最大开销是最大值
        while(left<right){
            long sum = 0;//需要的次数
            int mid = (left+right)/2;//全部处理到当前规模
            for(int &x:nums)//找规律得到处理次数
                sum+=ceil(x*1.0/mid)-1;
            if(sum>maxOperations) left = mid + 1;//这里要最小化该mid值,即满足条件也要取下界
            else right = mid;
        }
        return right;
    }
};

17. 制作 m 束花所需的最少天数

二分法+贪心获取最多相邻块数
class Solution {
public:
    int minDays(vector<int>& bloomDay, int m, int k) {
        if((long)m*k>bloomDay.size()) return -1;
        int left = 1;  int right = *max_element(bloomDay.begin(),bloomDay.end());
        while(left<right){
            int mid = (left+right)/2;
            int cur = 0;//当前天数下收集到的花束
            int sum = 0;//当前积累的花朵
            for(int i=0;i<bloomDay.size();i++){
                if(bloomDay[i]<=mid) sum++;
                else sum = 0;
                if(sum==k) sum=0,cur++;
            }
            if(cur>=m) right = mid;//相等也要往下取,取下界
            else left = mid + 1;
        }
        return right;
    }
};

18. 可以到达的最远建筑

二分+前缀和优化+优先队列
class Solution {
public:
    int furthestBuilding(vector<int>& heights, int bricks, int ladders) {
        if(ladders>=heights.size()-1) return heights.size()-1;//梯子数量足够
        int left = 0; int right = heights.size()-1;
        //预先计算楼梯差值
        vector<int> need(heights.size());
        vector<int> index(heights.size());//优化,记录到目前为止最大落差位置
        vector<long> presum(heights.size());//前缀和优化计算
        int max_ = INT_MIN;
        //预计算,优化记录
        for(int i=1;i<heights.size();i++){
            need[i] = heights[i]-heights[i-1]>0?heights[i]-heights[i-1]:0;
            presum[i] = presum[i-1] + need[i];//前缀和
        }
        while(left<right){
            int mid = (left+right+1)/2;//mid是当前终点
            if(mid<=ladders){ left = mid;continue;}

            long sum = presum[mid];//爬到终点的总代价
            priority_queue<int> q;//大顶堆找前n个最大值
            for(int i=1;i<=mid;i++)
                q.push(need[i]);
            for(int i=0;i<ladders;i++){
                sum = sum-q.top();
                q.pop();
            }

            if(sum>bricks) right = mid-1;
            else left = mid ;//尽可能跑远
        }
        return left;
    }
};

19. 可移除字符的最大数目

二分法+暴力
class Solution {
public:
    bool judge(string s,string &p){
        int i=0; int j=0;
        while(i<s.size()&&j<p.size()){
            if(s[i]==p[j]) j++;
            i++;
        }
        if(j==p.size()) return true;
        return false;
    }
    int maximumRemovals(string s, string p, vector<int>& removable) {
        int left = 0; int right = removable.size();//下标及以前的数都删除
        while(left<right){
            int mid = (left+right+1)/2;
            string temp = s;
            for(int i=0;i<mid;i++)
                temp[removable[i]] = '#';
            if(judge(temp,p)) left = mid;//满足条件也要往上取
            else right = mid-1;
        }
        return left;
    }
};

20. 最小化数组中的最大值

二分+贪心+滚动变量优化
class Solution {
public:
    int minimizeArrayValue(vector<int>& nums) {
        long long sum = accumulate(nums.begin(),nums.end(),0ll);
        long left = sum/nums.size(); long right = *max_element(nums.begin(),nums.end());
        while(left<right){
            long mid = (left+right)/2;
            long trans = 0;
            long long cur;
            for(int i=nums.size()-1;i>0;i--){
                cur = (long)nums[i]+trans;
                trans = cur>mid?cur-mid:0;
            }
            if(nums[0]+trans>mid) left = mid+1;
            else right = mid;
        }
        return right;
    }
};

21. 最小化数组中的最大值

点击查看代码

posted @ 2022-07-30 20:55  失控D大白兔  阅读(18)  评论(0编辑  收藏  举报