问题描述
小S在码头租用货船,有 Q 种不同类型的货船可供选择。每种货船有固定的数量 m[i]、租赁成本 v[i] 和最大载货量 w[i]。小S希望在预算 V 元内,租用能够承载最大总货物的货船组合。每种货船的具体信息包括数量、租赁价格和载货量。小S需要你帮忙计算在给定预算下,她能租用的货船的最大总载货量是多少。
Q: 货船的种类数量。V: 李华可用的总预算(单位:元)。ships: 一个列表,其中每个元素是一个元组[m[i], v[i], w[i]],分别表示第i种货船的数量、租赁价格和每艘货船的最大载货量。 这个问题是一个典型的0-1背包问题,其中我们需要在不超过给定预算的情况下最大化总载货量。我们可以使用动态规划来解决这个问题。
思考过程
动态规划解决方案
- 定义状态:设
dp[j]表示在预算为j元时能够达到的最大载货量。 - 状态转移方程:对于每种货船
i,我们考虑是否将其加入到当前的组合中。如果加入,我们需要检查是否有足够的预算来租用至少一艘这种类型的货船,并且更新当前的最大载货量。 - 初始化:
dp[0] = 0,因为没有预算时,最大载货量为0。 - 遍历每种货船:对于每种货船,我们遍历所有可能的预算,从0到V,更新
dp数组。 - 返回结果:
dp[V]将是在给定预算下能够达到的最大载货量。
代码提示
-
动态规划数组的初始化:
- 当前代码中,动态规划数组
dp的大小是[Q + 1][V + 1],这是正确的。但是,你需要确保在初始化时,dp[0][j]和dp[i][0]的值是合理的。通常情况下,dp[0][j]应该初始化为0,表示在没有选择任何货船时,最大载货量为0。
- 当前代码中,动态规划数组
-
动态规划状态转移:
- 当前代码中,状态转移方程是
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * price] + k * weight)。这个方程是正确的,但你可以考虑优化内层循环的边界条件,以减少不必要的计算。
- 当前代码中,状态转移方程是
-
边界条件检查:
- 在处理每种货船时,确保
k * price <= j的边界条件是正确的。如果k * price > j,则不需要继续计算。
- 在处理每种货船时,确保
核心代码示例
int[][] dp = new int[Q + 1][V + 1]; // 创建二维动态规划数组
for (int i = 1; i <= Q; i++) {
List<Integer> ship = ships.get(i - 1);
int num = ship.get(0);
int price = ship.get(1);
int weight = ship.get(2);
for (int j = 1; j <= V; j++) {
for (int k = 0; k <= num && k * price <= j; k++) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * price] + k * weight);
}
}
}
return dp[Q][V];
总结
- 我们首先初始化一个长度为
V + 1的数组dp,所有元素都设置为0。 - 对于每种货船,我们从
V到v的逆序遍历预算,以确保不会重复使用同一艘船。 - 对于每个预算
j,如果它可以被当前货船的租赁价格v整除,我们就更新dp[j]为dp[j - v] + w和当前dp[j]的最大值。 - 最后,
dp[V]将是在给定预算下能够达到的最大载货量。
这种方法确保我们考虑了所有可能的货船组合,并选择了在给定预算下能够最大化载货量的组合。