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及其后元素的个数;