算法---二分答案

算法步骤

  • 估计答案可能得范围是什么
  • 分析问题的答案和给定条件之间的单调性
  • 建立 check 函数,判断在答案固定的情况下条件是否达标
  • 在可行的范围内不断二分找到最适合的答案
最大最小

https://leetcode.cn/problems/split-array-largest-sum/description/

class Solution {
public:
    int splitArray(vector<int>& nums, int k) {
        long sum = 0;
        for (int x : nums)
            sum += x;
        long ans = 0;
        long l = *max_element(nums.begin(), nums.end());
        long r = sum;
        while (l <= r) {
            long m = (l + r) / 2;      
            long need = check(nums, m);
            if (need <= k) 
            {
            	// 为什么小于等于的都要记录?
            	/*
            	7 5
            	2 3 1 1 1 1 1
            	这种情况最后可能check回来得到的答案是4,但是可以拆成五个又不影
            	响最大值,也就是如果最后的这个 ans 由 check 函数求出来不是 k,
            	那么一定可以拆成 k 个且不影响最大值,拆成 k 个是必然的,因为 k 
            	始终小于等于 n ,如果影响了最大值,那么说明最大值还可以更小,这与
            	ans 是最终答案矛盾。 
            	*/
                ans = m;
                r = m - 1;
            } else
                l = m + 1;
        }
        return int(ans);
    }
    int check(const vector<int>& nums, int limit) {
        int need = 1;
        int sum = 0;
        for (int x : nums) {
            if (sum + x > limit)
                need++, sum = x;
            else
                sum += x;
        }
        return need;
    }
};
找出第k小数对的距离

考虑二分,check函数采用双指针的方式来找。

class Solution {
public:
    int smallestDistancePair(vector<int>& nums, int k) {
        sort(nums.begin(),nums.end()); // 升序排列
        int n=nums.size();
        int l=0,r=nums[n-1]-nums[0]; // 确定边界
        int ans=0;
        while(l<=r) // 二分找第 k 小的数
        { 
            int mid=(l+r)>>1; 
            int cnt=check(nums,mid);
            if(cnt>=k) // 如果当前数对的个数大于 k 往左边找
            {
                ans=mid;
                r=mid-1;
            } 
            else l=mid+1;
        }
        return ans;
    }
    int check(const vector<int>nums,int m)
    {
        int ans=0;
        int r=0;
        for(int l=0;l<nums.size();l++)
        {
            while(r+1<nums.size()&&nums[r+1]-nums[l]<=m)
            r++;
            ans+=r-l;
        }
        return ans;
    }
};

同时运行 N 台电脑的最长时间

class Solution {
public:
    long long maxRunTime(int n, vector<int>& batteries) {
       long long l=0,r=0;
        for(int x:batteries) // 确定右边界,即所有电池的总电量,由于电脑数很可能比电池的个数要少,因此用所有电池的总电量来作为右边界
        r+=x;
        long long ans;
        while(l<=r)
        {
            long long mid=l+(r-l)/2;
            if(check(batteries,mid,n))
            {
                ans=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        return ans;
    }
    bool check(vector<int>&batteries,long long limit,int num)
    {
        long long sum=0;
        for(int x:batteries)
        {
            if(x>=limit) num--; // 对于电量大于 limit 的电池,多出来的电量没法用给其他的电脑。
            else sum+=x;
            if(sum>=(long long)limit*num) return true;
        }
        return false;
    }
};
  • 9
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值