质检员的烦恼-vivo

文章讲述了质检员小V在vivoX80手机质量控制中,如何通过优化分组和称重策略,利用暴力回溯法、记忆化搜索以及剪枝技巧,以最短时间找到一台204克的不合格手机。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

质检员的烦恼 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;
    }
};
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值