一、水果店果篮最小成本问题
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、解题步骤
-
初始化dp数组:创建一个大小为n+1的dp数组,并将所有元素初始化为Integer.MAX_VALUE,除了dp[0]初始化为0。
-
遍历每个水果:从1到n遍历每个水果。
-
尝试打包:对于每个水果i,从1到m尝试将其与前面的j个水果打包成一个果篮。
-
计算成本:对于每个果篮,计算其中的最大体积和最小体积,然后根据公式计算成本。
-
更新dp数组:将当前果篮的成本加到dp[i-j]上,与dp[i]进行比较,取较小值。
-
返回结果:最后,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)。此外,我们还可以考虑使用滚动数组来减少空间复杂度。但是,这些优化可能会使代码变得复杂,因此在实际应用中我们需要权衡优化和代码的可读性。