二分查找 875. 爱吃香蕉的珂珂 +1300. 转变数组后最接近目标值的数组和

875. 爱吃香蕉的珂珂

珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。

珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。

珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。

返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。

示例 1:

输入: piles = [3,6,7,11], H = 8
输出: 4

示例 2:

输入: piles = [30,11,23,4,20], H = 5
输出: 30

示例 3:

输入: piles = [30,11,23,4,20], H = 6
输出: 23

提示:

1 <= piles.length <= 10^4
piles.length <= H <= 10^9
1 <= piles[i] <= 10^9

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/koko-eating-bananas
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题
二分查找需要的时间,若时间超出,则增大速度,反之速度不变,减小边界即可;

class Solution {
public:
    int minEatingSpeed(vector<int>& piles, int H) {
        long long sum=0;
        for(int i:piles) sum+=i;
        long long left=1;
        long long right =sum;
        long long mid;
        long long t;
        while(left<right)
        {
            mid = left+(right-left)/2;
            t=0;
            for(int i:piles) t+=(i-1)/mid+1;  //吃完香蕉所需的时间
            if(t>H) left=mid+1;  //所需时间过大——需要+1  
            else right=mid;
        }
        return left;
    }
};

注意点
每个组吃完香蕉的时间为(i-1)/mid +1 ,因为mid刚好为这个框的香蕉值时可能可以整除i,所以需要i-1;

longlong保存,int保存不下;

1300. 转变数组后最接近目标值的数组和

给你一个整数数组 arr 和一个目标值 target ,请你返回一个整数 value ,使得将数组中所有大于 value 的值变成 value 后,数组的和最接近 target (最接近表示两者之差的绝对值最小)。

如果有多种使得和最接近 target 的方案,请你返回这些整数中的最小值。

请注意,答案不一定是 arr 中的数字。

示例 1:

输入:arr = [4,9,3], target = 10
输出:3

解释:当选择 value 为 3 时,数组会变成 [3, 3, 3],和为 9 ,这是最接近 target 的方案。
示例 2:

输入:arr = [2,3,5], target = 10
输出:5

示例 3:

输入:arr = [60864,25176,27249,21296,20204], target = 56803
输出:11361

提示:

1 <= arr.length <= 10^4
1 <= arr[i], target <= 10^5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-of-mutated-array-closest-to-target
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题
二分查找,下底为0,上底为nums中最大的数,或直接设置为target,二分查找范围对速度影响不大;

每次取mid,计算总和O(N),比较总和和target差的绝对值,绝对值小的保存该mid,绝对值相等但mid小于结果的也保存mid;
复杂度O(NlogN);

class Solution {
public:
    int findBestValue(vector<int>& arr, int target) {
        long long left=0;
        long long right = target;
        long long sum=0;
        long long mid;
        long long distance=target;
        long long res;
        while(left<right)
        {
            sum=0;
            mid = left+(right-left)/2;
            for(int i:arr) {
                if(i>mid) sum+=mid;
                else sum+=i;
            }
            if(abs(sum-target)<distance||mid<res&&abs(sum-target)==distance) //判断距离
            {
                distance=(abs(sum-target));
                res=mid;     //保存结果
            }
            if(sum<target) left=mid+1;   //从小往大找
            else right=mid;
            
        }
        return res;
    }
};
class Solution {
public:
    int findBestValue(vector<int>& arr, int target) {
        int right=*max_element(arr.begin(),arr.end())+1;  //不加一则无法遍历上底
        int left=0;
        int mindis=INT_MAX;
        int res=0;
        while(left<right){
            int mid=left+(right-left)/2;
            int tmp=cal(arr,mid);
            if(abs(tmp-target)<mindis||(mid<res&&abs(tmp-target)==mindis)) {
                mindis=abs(tmp-target);
                res=mid;
            }
            if(tmp<target) left=mid+1;
            else right=mid;
        }
        return res;
    }
private:
    int cal(vector<int>&arr , int value){
        int s=0;
        for(auto t:arr)
            s+= value<t? value:t;
        return s;
    }
};

二分查找+二分查找
二分查找计算每次mid前和mid后的和;
再二分查找mid;

实现对数组进行排序+计算前缀和;

class Solution {
public:
    int findBestValue(vector<int>& arr, int target) {
        sort(arr.begin(),arr.end());
        int n=arr.size();
        preflix.resize(n+1,0);
        for(int i=1;i<=n;i++)
            preflix[i]= preflix[i-1]+arr[i-1];
        //preflix[n]为前n个数的和
        int left=0;
        int right=arr[n-1];
        while(left<right){
            int mid=left+(right-left+1)/2;
            //找到比target小的mid
            auto iter=lower_bound(arr.begin(),arr.end(),mid);  //找到小于mid的最后一个数
            int tmp=preflix[iter-arr.begin()]+mid*(arr.end()-iter);
            if(tmp>=target) right=mid-1;  
            else left=mid;
        }
        //找到的left<target
        auto iter1=lower_bound(arr.begin(),arr.end(),left);
        int res1=preflix[iter1-arr.begin()]+left*(arr.end()-iter1);
        auto iter2=lower_bound(arr.begin(),arr.end(),left+1);
        int res2=preflix[iter2-arr.begin()]+(left+1)*(arr.end()-iter2);
        
        return abs(res1-target)>abs(res2-target)? left+1:left;
    }
private:
    vector<int> preflix;
};

二分出来后left为和比target小的最大值;
注意点
1 函数lower_bound()

功能:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置.

注意:如果所有元素都小于val,则返回last的位置,且last的位置是越界的!!

2 函数upper_bound()

功能:函数upper_bound()返回的在前闭后开区间查找的关键字的上界,返回大于val的第一个元素位置

注意:返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置。同样,如果val大于数组中全部元素,返回的是last。(注意:数组下标越界)

iter-arr.begin()得到iter前元素的个数;
arr.end()-iter得到iter及其后元素的个数;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值