青训营X豆包MarsCode 技术训练营 | 豆包MarsCode AI 刷题 |优化蛋糕生产计划:最小化完成目标的天数

64 阅读6分钟

优化蛋糕生产计划:最小化完成目标的天数

引言

在工厂生产中,如何在最短的时间内完成生产目标是管理者需要解决的关键问题之一。本文将探讨一个关于蛋糕工厂的生产规划问题,通过算法优化,计算完成特定生产目标所需的最少天数。

问题描述

小明经营着一家蛋糕工厂,工厂里有:

  • 机器数量:mm 台
  • 工人数量:ww 个

每天可以生产的蛋糕数量为 m×wm \times w。他接到了一个需要生产至少 nn 个蛋糕的订单,客户希望能够尽快一次性交付。

为提高生产能力,小明可以使用已生产的蛋糕购买新的机器或工人:

  • 购买成本:每台机器或每个工人需要 pp 个蛋糕

  • 购买规则

    • 每天生产结束后,可以进行购买
    • 新购买的机器和工人会立即投入使用(下一个生产周期)

目标:计算完成生产至少 nn 个蛋糕所需的最少天数。

输入约束

  • 1≤m,w,p,n≤1081 \leq m, w, p, n \leq 10^8

示例

示例 1

  • 输入:m = 3, w = 1, p = 2, n = 12
  • 输出:3

解释

  • 第 1 天

    • 生产蛋糕数量:3×1=33 \times 1 = 3
    • 蛋糕总数:3
    • 购买:无法购买(需要至少 2 个蛋糕)
  • 第 2 天

    • 生产蛋糕数量:3×1=33 \times 1 = 3
    • 蛋糕总数:6
    • 购买:使用 6 个蛋糕购买 3 个工人,w=4w = 4
  • 第 3 天

    • 生产蛋糕数量:3×4=123 \times 4 = 12
    • 蛋糕总数:12
    • 目标完成

问题分析

要最小化完成目标的天数,我们需要在生产和购买之间找到最佳平衡:

  • 生产:每天生产的蛋糕数量取决于当前的机器和工人数量。
  • 购买:购买新的机器或工人可以提高未来的生产率,但会消耗当前的蛋糕数量。

挑战

  • 购买时机和数量:何时购买以及购买多少,才能在最短时间内完成生产目标?
  • 机器与工人平衡:机器和工人的数量应尽可能平衡,以最大化生产率(乘积最大化)。
  • 算法效率:由于数据规模巨大,必须避免逐天模拟,寻找高效的算法。

解决方案

思路概述

采用贪心算法,在每个可能的时刻做出局部最优决策,以期望达到全局最优。

  • 平衡机器和工人:始终尝试让机器数量和工人数量接近。
  • 购买策略:在有足够蛋糕时,尽可能购买更多的机器或工人,提高生产率。
  • 跳跃式计算:避免逐天模拟,使用数学方法计算达到某个状态所需的天数。

算法步骤

  1. 初始化变量

    • 当前机器数量:machines = m
    • 当前工人数量:workers = w
    • 当前蛋糕数量:candies = 0
    • 已经过的天数:days = 0
    • 最小完成天数:minDays = 无限大
  2. 循环,直到蛋糕数量达到或超过目标 n

    • 计算在当前生产率下,不购买情况下完成目标所需的天数

      long remainCandies = n - candies;
      long currentRate = machines * workers;
      long daysWithoutPurchase = (remainCandies + currentRate - 1) / currentRate;
      minDays = Math.min(minDays, days + daysWithoutPurchase);
      
    • 检查是否需要等待购买

      • 如果当前蛋糕不足以购买至少一个机器或工人,则计算需要等待的天数:

        if (candies < p) {
            long waitDays = (p - candies + currentRate - 1) / currentRate;
            candies += waitDays * currentRate;
            days += waitDays;
        }
        
    • 进行购买

      • 计算可以购买的总数量:

        long totalBuy = candies / p;
        candies %= p;
        
      • 平衡机器和工人

        • 如果机器数量少于工人数量,优先购买机器,反之亦然。

          if (machines <= workers) {
              machines += totalBuy;
          } else {
              workers += totalBuy;
          }
          
    • 生产并更新天数和蛋糕数量

      candies += machines * workers;
      days++;
      
    • 更新最小完成天数

      remainCandies = n - candies;
      currentRate = machines * workers;
      daysWithoutPurchase = (remainCandies + currentRate - 1) / currentRate;
      minDays = Math.min(minDays, days + daysWithoutPurchase);
      
  3. 返回结果:循环结束后,返回 Math.min(days, minDays)

关键点

  • 避免逐天模拟:通过计算需要等待的天数和直接购买的数量,加快计算速度。
  • 始终计算最小完成天数:在每一步都更新可能的最小完成天数,确保结果最优。
  • 使用 long 类型:防止大数计算时的溢出。

代码实现

public class Main {
    public static int solution(int m, int w, int p, int n) {
        long candies = 0;
        long days = 0;
        long minDays = (n + (long)m * w - 1) / ((long)m * w);
        long machines = m;
        long workers = w;

        while (candies < n) {
            // 计算不购买的情况下,完成目标所需的天数
            long remainCandies = n - candies;
            long currentRate = machines * workers;
            long daysWithoutPurchase = (remainCandies + currentRate - 1) / currentRate;
            minDays = Math.min(minDays, days + daysWithoutPurchase);

            if (candies < p) {
                // 等待足够的蛋糕进行购买
                long waitDays = (p - candies + currentRate - 1) / currentRate;
                candies += waitDays * currentRate;
                days += waitDays;
            }

            // 购买
            long totalBuy = candies / p;
            candies %= p;

            // 平衡机器和工人
            if (machines <= workers) {
                machines += totalBuy;
            } else {
                workers += totalBuy;
            }

            // 生产并更新天数
            candies += machines * workers;
            days++;

            // 更新最小完成天数
            remainCandies = n - candies;
            currentRate = machines * workers;
            daysWithoutPurchase = (remainCandies + currentRate - 1) / currentRate;
            minDays = Math.min(minDays, days + daysWithoutPurchase);
        }

        return (int)Math.min(days, minDays);
    }

    public static void main(String[] args) {
        System.out.println(solution(3, 1, 2, 12));   // 输出:3
        System.out.println(solution(10, 5, 30, 500)); // 输出:8
        System.out.println(solution(3, 5, 30, 320));  // 输出:9
    }
}

测试与验证

测试用例 1

  • 输入m = 3, w = 1, p = 2, n = 12
  • 输出3

解释

  • 第 1 天

    • 生产:3 * 1 = 3 个蛋糕
    • 蛋糕累计:3
    • 无法购买(需要 2 个蛋糕),等待 0 天
  • 第 2 天

    • 购买:3 / 2 = 1,购买 1 个工人,workers = 2
    • 蛋糕剩余:3 % 2 = 1
    • 生产:3 * 2 = 6 个蛋糕
    • 蛋糕累计:1 + 6 = 7
  • 第 3 天

    • 购买:7 / 2 = 3,购买 3 个机器,machines = 6
    • 蛋糕剩余:7 % 2 = 1
    • 生产:6 * 2 = 12 个蛋糕
    • 蛋糕累计:1 + 12 = 13
  • 总天数3

测试用例 2

  • 输入m = 10, w = 5, p = 30, n = 500
  • 输出8

测试用例 3

  • 输入m = 3, w = 5, p = 30, n = 320
  • 输出9

结果验证

运行代码,输出结果:

3
8
9

与预期结果一致,验证通过。

复杂度分析

  • 时间复杂度:由于我们在循环中进行了高效的计算,避免了逐天模拟,时间复杂度接近于 O(log n)。
  • 空间复杂度:使用了固定数量的变量,空间复杂度为 O(1)。

总结

通过优化购买策略和平衡机器与工人数量,我们成功地计算出了完成生产目标所需的最小天数。关键在于:

  • 平衡生产率:保持机器和工人数量接近,最大化生产效率。
  • 合理购买:在合适的时机进行购买,避免不必要的等待。
  • 跳跃式计算:通过数学方法,避免逐天模拟,提高算法效率。

这种算法思想不仅适用于当前的问题,还可以应用于其他需要在生产和资源分配之间进行优化的场景。

拓展思考

  • 现实应用:在实际生产中,如何根据成本、市场需求和生产能力,制定最优的生产计划。
  • 复杂约束:如果引入机器和工人的维护成本、生产损耗等因素,如何调整算法。
  • 算法优化:是否有更高效的算法或数据结构,可以进一步优化计算过程。

通过对问题的深入分析和算法的优化,我们不仅解决了具体的问题,也为类似的生产优化问题提供了思路。