质检员的烦恼 vivo23秋招软件类1卷
每一款vivoX80的手机质量必须控制在无误差的203克。
假设,质检员小V需要从N台X80手机中选出一台质量为204克的不合格手机。
质检员挑选不合格手机的步骤如下:
1、分组:质检员每轮选择一个正整数,假设当前选择的正整数为K,那么将当前未排除嫌疑的手机进行每组K个的分组,如果不能整除K,那么将最后剩余的手机再单独分一组。
2、称重,以步骤1中的每组为单位,称重设备将对每组都进行一次总质量称重。称重完成后,根据计算你就可以确定不合格手机位于哪一组中。
3、排除掉合格的手机组,循环1、2两步骤,直到嫌疑手机仅剩一台。
总花费时间为每一轮花费的时间总和,每一轮的花费时间由以下两部分组成:
a、每台手机都需要移动到称重设备处,假定该轮共X台手机,题目给定单台手机的移动时间A,那么该轮次的移动时间为 X乘以A。
b、称重需要时间,每一组都需要称重,假定当前轮次共被分成了G组,题目给定每组称重的时间B,那么该轮次的称重时间为G乘以B。
现在给定手机的个数N,单台手机移动到称重处的时间A,每组称重需要的时间B。
请帮质检员计算,每轮应该怎样选择正整数K,才能在运气最差的情况下用最短的时间找到不合格的手机?题目要求你输出该最短的时间。
我们假定运气最差的语义为:每一轮都是数量最多的组进入了下一轮。
暴力回溯法
从1个一组开始尝试,1,2,3…n
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 计算质检员每轮应该如何选择正整数K,才能在运气最差的情况下用最短的时间找到不合格的手机?并输出该最短的时间。
* @param n int整型 手机个数N
* @param a int整型 单个移动时间A
* @param b int整型 单组称重时间B
* @return long长整型
*/
long long minTime(int n, int a, int b) {
// write code here
long long time, minTime1=n*(a+b);
for(long long k=2;k<n;++k){
long long group = n/k+(n%k!=0?1:0);
time = n*a+ group*b + minTime(k,a,b);
// cout<<"n,k,gp,time:"<<n<<" "<<k<<" "<<group<<" "<<time<<endl;
minTime1=min(time,minTime1);
}
return minTime1;
}
};
回溯法的三种优化
记忆化搜索
比如:60的有因子2、3、10
假设先计算2,最后计算10;先计算3,最后也计算10。
那么10的计算,就是重复的,可以使用备忘录存储起来。
回溯剪枝
剪枝1:如果初始 n=50,k取25,26,27… group为50/25, 50/26, 50/27… 都是2组,但是整除的k是最小的。也就是说,第一次被分为2组的值是最小的,后面的可以进行剪枝。
剪枝2:回溯法的本质是dfs,记录dfs全局最小值。如果当前cost已经超过全局最小值,那么剪枝。
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿 修改,直接返回方法规定的值即可
*
* 计算质检员每轮应该如何选择正整数K,才能在运气最差的情况下用最短的时间找到不合格的手机?并输出该最短的时间。
* @param n int整型 手机个数N
* @param a int整型 单个移动时间A
* @param b int整型 单组称重时间B
* @return long长整型
*/
const int length = 1000; //内存有限制
long long myMinTime(long long n, long long a, long long b, long long already_cost, long long& minCost, vector<long long>& dp) {
if(n<length&&dp[n]!=0){ //优化1:记忆化搜索
return dp[n];
}
long long lastGroup = n;
minCost = min(n*(a+b)+already_cost,minCost); //全局最值
long long leftMinCost = n*(a+b); //n剩余的最小值
for(long long k=2;k<n;++k){ //k个一组
long long group = n/k+(n%k!=0?1:0);
if(group==lastGroup){ //优化2:剪枝:如果初始 n=50,k取25,26,27... group为50/25 50/26 50/27... 都是2组,但是整除的k是最小的
continue;
}
long long cur_cost = n*a+ group*b + already_cost; //优化3:继续剪枝:如果已经超过全局最小值,那么剪枝
if(cur_cost >= minCost){
continue;
}
long long left=myMinTime(k,a,b, cur_cost, minCost,dp); //k a b的k可能是计算过的
minCost = min(minCost,left+cur_cost);
leftMinCost = min(leftMinCost, n*a+ group*b+left);
// cout<<"n,k,gp,time:"<<n<<" "<<k<<" "<<group<<" "<<time<<endl;
lastGroup=group;
}
if(n<length){
dp[n]=leftMinCost;
}
return leftMinCost;
}
long long minTime(int n, int a, int b) {
if(n==1){
return a+b;
}
vector<long long> dp(length);
long long minCost=LONG_LONG_MAX;
myMinTime(n,a,b, 0, minCost,dp);
return minCost;
}
};