今天给大家带来的是一道:小F的糖果工厂挑战;
先来看一下题目,看看题目要我们做一些什么事情:
理解题目要求:
- 工厂有n种糖果,每种糖果每天可以生产ci个。
- 需要生产a包糖果,每包糖果至少有b个。
- 我们需要计算至少需要多少天才能完成这个订单。
接着再来看一下这一道题的样例:
很好,我明白了,我管他的要多少天,我直接来一个暴力的做法,即:我们从第一天开始,逐天计算工厂能生产多少包糖果,直到满足订单要求,直接写一个O(nab)的算法复杂度不就可以了,简简单单,行了你不要讲了,我直接秒~
优缺点分析
优点:实现简单,容易理解。
缺点:时间复杂度高,为O(nab),在数据量大时效率极低。
额。。。你的意思就是我还要优化咯。。。肯定的,你要优雅一点,比如能不能随便给你一个天数,你检查一下看看能不能达到要求。
这还不简单,我直接拿你给的天数进行计算,看看你给的这个天数可以装多少包糖果不就好了,有什么难的~
- 这里我们先来定义一个check函数来对给定的天数进行检查:
- cnt表示该天数可以装的糖果;
- 计算该天数每一种糖果的数量;
- 对每一种糖果做一个除法向下取整即可(因为每一包糖果必须是同一种,所以不够装的我们就不要了)
具体代码如下:
public static boolean check(int n,int a,int b,int mid){
int cnt=0;
for(int i=0;i<n;++i){
cnt+=arr[i]*mid/b;
}
return cnt>=a;
}
接着就可以尝试用高效的算法来给定check一个天数:
二分查找优化思路
解题思路
由于暴力解法效率低下,我们可以考虑使用二分查找来优化。二分查找的核心思想是:在有序的区间内,通过不断缩小搜索范围来找到目标值。
确定搜索范围
我们知道,至少需要1天来完成订单,最多需要ab天(在最坏情况下,每天只生产一个糖果)。因此,我们的搜索范围是[1, ab]。
二分查找步骤
-
初始化搜索边界:
l = 1
,r = a * b
。 -
进行二分搜索:
- 计算中间值
mid = (l + r) / 2
。 - 使用
check
函数检查在mid
天内是否可以生产足够的糖果包。 - 如果可以,我们需要尝试看看能不能再少一点天数,即将搜索范围缩小到左半部分,即
r = mid
。 - 如果不可以,我们需要尝试看看能不能稍微多一点天数,即将搜索范围缩小到右半部分,即
l = mid
。
- 计算中间值
直到退出循环条件,即while(l+1!=r)
但是需要注意一点的是,最后r一定是可以的,但是l不一定可以(主要是我这个二分写法的不同导致的),因为假设mid一直可行,是否会出现一种l+1就是mid,而此时的mid还是可行,然后进行r=mid后,退出了循环(因为l+1==r),但是是不是l一直没有被检查是否可行,如果最后l可行,那你输出r就错误,所以我们最后还是需要check(l)一下!
到此,我们就完成了这一道题~,附上完整代码:
public class Main {
private static int[] arr;
public static int solution(int n, int a, int b, int[] candies) {
// write code here
arr=candies;
int l=1,r=a*b;
while(l+1<r){
int mid=(l+r)>>1;
if(check(n,a,b,mid)) r=mid;
else l=mid;
}
return check(n,a,b,l) ? l:r;
}
public static boolean check(int n,int a,int b,int mid){
int cnt=0;
for(int i=0;i<n;++i){
cnt+=arr[i]*mid/b;
}
return cnt>=a;
}
public static void main(String[] args) {
int[] candies1 = {7, 9, 6};
int[] candies2 = {3, 10, 8, 4};
int[] candies3 = {1, 10};
System.out.println(solution(3, 10, 20, candies1) == 10);
System.out.println(solution(4, 5, 15, candies2) == 4);
System.out.println(solution(2, 100, 5, candies3) == 46);
}
}