AI 刷题 水果店果篮最小成本问题 题解 | 豆包MarsCode AI刷题

117 阅读4分钟

一、水果店果篮最小成本问题

1、问题描述

小C开了一家水果店,某天接到了一个大订单,要求将n个编号为1到n的水果打包成多个果篮,每个果篮的最大容量为m个水果,并且果篮中水果的编号必须连续。每个果篮的成本与容纳的水果数量及水果的体积相关,成本公式为:

k×⌊(u+v)/2⌋+s

其中,u是果篮中水果的最大体积,v是果篮中水果的最小体积,k是果篮中水果的数量,s是一个常数,⌊x⌋ 表示对x进行下取整。

你的任务是帮助小C将这些水果打包成若干果篮,使得总成本最小。

例如:当有6个水果,体积为[1, 4, 5, 1, 4, 1],一个果篮最多装4个水果,常数s为3时,最优的打包方案是将前三个水果(1, 4, 5)装成一个果篮,后三个水果(1, 4, 1)装成另一个果篮,最小的总成本为21。

2、测试样例

样例1:

输入:n = 6, m = 4, s = 3, a = [1, 4, 5, 1, 4, 1]
输出:21

样例2:

输入:n = 5, m = 3, s = 2, a = [2, 3, 1, 4, 6]
输出:17

样例3:

输入:n = 7, m = 4, s = 5, a = [3, 6, 2, 7, 1, 4, 5]
输出:35

二、问题分析

这个问题是一个典型的动态规划问题,我们的目标是找到一种打包方式,使得总成本最小。

1、解题思路

  •  定义状态:设dp[i]表示前i个水果的最小总成本。
  •  状态转移:对于每个水果i,我们尝试将其与前面的j个水果(j从1到m)打包成一个果篮,计算这个果篮的成本,并更新dp[i]。
  •  初始化:dp[0] = 0,表示没有水果时成本为0。
  •  计算果篮成本:对于每个果篮,我们需要找到其中的最大体积maxVolume和最小体积minVolume,然后根据公式计算成本。
  •  更新状态:对于每个i,我们通过尝试不同的j,找到最小的dp[i]。

2、解题步骤

  1. 初始化dp数组:创建一个大小为n+1的dp数组,并将所有元素初始化为Integer.MAX_VALUE,除了dp[0]初始化为0。

  2.  遍历每个水果:从1到n遍历每个水果。

  3.  尝试打包:对于每个水果i,从1到m尝试将其与前面的j个水果打包成一个果篮。

  4.  计算成本:对于每个果篮,计算其中的最大体积和最小体积,然后根据公式计算成本。

  5.  更新dp数组:将当前果篮的成本加到dp[i-j]上,与dp[i]进行比较,取较小值。

  6.  返回结果:最后,dp[n]即为前n个水果的最小总成本。

三、代码实现

import java.util.Arrays;

public class Main {
    public static int solution(int n, int m, int s, int[] a) {
        // 初始化 dp 数组,dp[i] 表示前 i 个水果的最小总成本
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0; // 没有水果时成本为 0

        // 遍历每个水果
        for (int i = 1; i <= n; i++) {
            int maxVolume = Integer.MIN_VALUE;
            int minVolume = Integer.MAX_VALUE;

            // 尝试将当前水果与前面的水果打包成一个果篮
            for (int j = 1; j <= m && i - j >= 0; j++) {
                maxVolume = Math.max(maxVolume, a[i - j]);
                minVolume = Math.min(minVolume, a[i - j]);

                // 计算当前果篮的成本
                int cost = j * ((maxVolume + minVolume) / 2) + s;

                // 更新 dp[i]
                dp[i] = Math.min(dp[i], dp[i - j] + cost);
            }
        }

        // 返回前 n 个水果的最小总成本
        return dp[n];
    }

    public static void main(String[] args) {
        System.out.println(solution(6, 4, 3, new int[]{1, 4, 5, 1, 4, 1}) == 21);
        System.out.println(solution(5, 3, 2, new int[]{2, 3, 1, 4, 6}) == 17);
        System.out.println(solution(7, 4, 5, new int[]{3, 6, 2, 7, 1, 4, 5}) == 35);
    }
}

四、复杂度分析

  • 时间复杂度:O(n * m),因为我们需要遍历每个水果,并且对于每个水果,我们尝试将其与前面的m个水果打包成一个果篮。
  • 空间复杂度:O(n),因为我们需要一个大小为n+1的dp数组来存储状态。

五、总结

这个问题是一个典型的动态规划问题,通过定义状态和状态转移,我们可以找到最小成本的打包方式。在实际应用中,我们可能需要考虑更多的约束条件,例如水果的重量、果篮的大小等。

优化思考

虽然当前的代码已经可以解决问题,但是我们可以通过一些优化来提高效率。例如,我们可以使用单调队列来快速找到最大体积和最小体积,这样可以将时间复杂度降低到O(n)。此外,我们还可以考虑使用滚动数组来减少空间复杂度。但是,这些优化可能会使代码变得复杂,因此在实际应用中我们需要权衡优化和代码的可读性。