Day73 | 灵神 | 二分查找 最大合金数
笔者的个人博客网站:标签: 二分查找 | Darlingの妙妙屋
2861.最大合金数
注:
笔者用的是左闭右开区间
思路:
还是老套路,先看二分的是什么,这道题求的是最大合金数,那么二分的就是合金数,而不是其他的东西
看看单调性,如果合金的数量越大,消耗的钱数就越多,budget就余额不够用,我们就可以找到最大值
再看二分区间,即合金数的取值范围
假设一个金属所需要的所有材料都是1,即comp数组全是1,花费cost也全是1,那最大值就是min(stock)+budget
取到最大值的情况就是,一个合金只需要一个金属并且花费也是1,这时候可以取到最大值
也可以是,最小的那个stock那个材料花费是1,所需也是1,但是其他的stock的值全都大于最小的那个stock+budget,即可以把min(stock)+budget给用完
可能的疑惑:
为什么最大值是min(stock)也不是max呢?
短板效应,我们拥有的最小的stock才是限制我们的能制作的合金数量的关键
看完二分区间再来看看判断条件,即check函数该怎么写,这个也是最难的部分
我们二分的是合金数量,那么我们的mid自然就是合金数量,而我们的check函数判断的就是有budget这个预算和stock即我们拥有的金属这两个条件下,可不可以合成mid这么多的数量,如果可以合成,那么说明mid符合条件,我们可以继续往大的放大mid,即l=mid+1,合成不了mid这么多,那就说明mid大了,要往小的走
那么我们如何判断是否可以合成mid那么多的合金呢?
要合成mid数量的合金,我们每个金属需要的量是nums=comp[i]*mid,这是每个金属需要的数量
而如果nums<=stock[i],那说明我们本身就有那么多的i种金属,那就不需要买了
如果nums>stock[i],那说明我们本身没那么多,那就得补足剩下的 即 买(nums-stock[i])个该类金属
花费(nums-stock[i])*cost[i]
如果补足所有金属一共花费的钱比我们的预算多,那就合成不了mid个合金,返回false,如果少的话那就可以合成,返回true.
完整代码:
注:防止溢出需要用long lonng
class Solution {
public:
//这种的 mid,budget,costs都得是longlong
bool check(vector<int>& v,vector<int>& stock,vector<int>&cost,long long mid,long long budget)
{
//在v的条件下最多可以制造多少合金
for(int i=0;i<stock.size();i++)
{
if(stock[i]<mid*v[i])
{
long long costs=(mid*v[i]-stock[i])*cost[i];
budget-=costs;
}
}
return budget>=0;
}
/*
//这种写法budget不需要是longlong
bool check(vector<int>& v,vector<int>& stock,vector<int>&cost,long long mid,int budget)
{
//在v的条件下最多可以制造多少合金
long long costs=0;
for(int i=0;i<stock.size();i++)
{
if(stock[i]<mid*v[i])
{
costs+=(long long)(mid*v[i]-stock[i])*cost[i];
if(costs>budget)
return false;
}
}
return true;
}*/
int maxNumberOfAlloys(int n, int k, int budget, vector<vector<int>>& composition, vector<int>& stock, vector<int>& cost)
{
int res=0;
for(auto v:composition)
{
int l=0,r=ranges::min(stock)+budget+1;
while(l<r)
{
int mid=l+(r-l)/2;
if(check(v,stock,cost,mid,budget))
l=mid+1;
else
r=mid;
}
res=max(res,l-1);
}
return res;
}
};