问题分析
本问题可以看作一个典型的背包问题,但是与常见的 0/1 背包或完全背包不同的是,这里每种类型的货船都有数量限制。因此,我们需要使用动态规划来求解这个问题。任务是根据预算,在不同的货船类型中选择,达到最大载货量。
思路解析
-
动态规划:我们可以使用动态规划来解决背包问题。设
dp[i][j]表示在前i种货船中,预算为j时所能达到的最大载货量。 -
状态转移:
- 对于每种类型的货船(设它的数量是
m[i],租赁价格是v[i],最大载货量是w[i]),我们可以选择不租用该类型货船,或者租用一定数量的该类型货船。对于每种情况,我们需要计算能承载的最大货物量。
- 对于每种类型的货船(设它的数量是
-
完全背包的扩展:当有多种货船时,可以通过迭代不同数量的货船来处理。由于每种货船的数量是有限的,这就涉及到多重背包问题,可以通过转化为完全背包的问题来处理。
-
递推关系:
- 对于每个状态
dp[i][j](表示预算为j时),可以从前一个状态dp[i-1][j-k*v[i]] + k*w[i]更新得到,其中k是我们可以选择的货船数量。
- 对于每个状态
解法实现
我们可以使用一个二维的 dp 数组,其中 dp[i][j] 表示考虑前 i 种货船,且预算为 j 时所能承载的最大载货量。
- 初始化
dp[0][j]为0(表示没有货船可租时,载货量为0)。 - 对于每种类型的货船,进行逐步更新。
import java.util.ArrayList;
import java.util.List;
public class Main {
public static int solution(int Q, int V, List<List<Integer>> ships) {
// dp[i][j] 表示前 i 种货船,预算为 j 时能得到的最大载货量
int[][] dp = new int[Q + 1][V + 1];
// 遍历每一种类型的货船
for (int i = 1; i <= Q; i++) {
int m = ships.get(i - 1).get(0); // 该类型货船的数量
int v = ships.get(i - 1).get(1); // 该类型货船的租赁价格
int w = ships.get(i - 1).get(2); // 该类型货船的最大载货量
// 遍历每一个可能的预算
for (int j = 0; j <= V; j++) {
// 先考虑不选择当前类型的货船
dp[i][j] = dp[i - 1][j];
// 遍历当前类型货船的租赁数量
for (int k = 1; k <= m && k * v <= j; k++) {
// 更新 dp[i][j],表示在当前预算下,选择 k 艘该类型货船的最优方案
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * v] + k * w);
}
}
}
// 返回考虑所有货船种类并且预算为 V 时,能够承载的最大载货量
return dp[Q][V];
}
public static void main(String[] args) {
// 测试用例
List<List<Integer>> ships = new ArrayList<>();
ships.add(List.of(2, 3, 2));
ships.add(List.of(3, 2, 10));
List<List<Integer>> ships2 = new ArrayList<>();
ships2.add(List.of(30, 141, 47));
ships2.add(List.of(9, 258, 12));
ships2.add(List.of(81, 149, 13));
ships2.add(List.of(91, 236, 6));
ships2.add(List.of(27, 163, 74));
ships2.add(List.of(34, 13, 58));
ships2.add(List.of(61, 162, 1));
ships2.add(List.of(80, 238, 29));
ships2.add(List.of(36, 264, 28));
ships2.add(List.of(36, 250, 2));
ships2.add(List.of(70, 214, 31));
ships2.add(List.of(39, 116, 39));
ships2.add(List.of(83, 287, 4));
ships2.add(List.of(61, 269, 94));
ships2.add(List.of(23, 187, 46));
ships2.add(List.of(78, 33, 29));
ships2.add(List.of(46, 151, 2));
ships2.add(List.of(71, 249, 1));
ships2.add(List.of(67, 76, 85));
ships2.add(List.of(72, 239, 17));
ships2.add(List.of(61, 256, 49));
ships2.add(List.of(48, 216, 73));
ships2.add(List.of(39, 49, 74));
System.out.println(solution(2, 10, ships) == 32);
System.out.println(solution(23, 400, ships2) == 1740);
}
}
代码详解
-
输入参数处理:
Q是货船的种类数。V是可用的总预算。ships是一个列表,包含每种货船的数量、租赁价格和最大载货量。
-
动态规划数组
dp:dp[i][j]表示前i种货船,且预算为j时的最大载货量。- 初始化:
dp[0][j] = 0,即没有货船时载货量为0。
-
遍历所有货船类型:对每种货船类型进行遍历。
- 对每种货船类型,我们通过
k来表示租用k艘该类型货船的情况,更新dp数组。
- 对每种货船类型,我们通过
-
时间复杂度:
- 假设有
Q种货船,最大预算为V,每种货船最多选择m[i]艘。 - 内层循环最多迭代
m[i]次,外层循环遍历Q种货船,预算最大为V。 - 最终的时间复杂度是 O(Q * V * m),其中
m是每种货船的数量上限。
- 假设有
复杂度分析
- 时间复杂度:O(Q * V * m),其中
Q是货船种类的数量,V是预算,m是每种货船的数量。 - 空间复杂度:O(Q * V),因为我们需要一个二维数组
dp来存储每种状态的最大载货量。
样例分析
样例 1:
输入:
Q = 2, V = 10, ships = [[2, 3, 2], [3, 2, 10]]
- 第一种货船:最多 2 艘,每艘价格 3 元,载货量 2 吨。
- 第二种货船:最多 3 艘,每艘价格 2 元,载货量 10 吨。
- 最大载货量为 32 吨。
样例 2:
输入:
Q = 3, V = 50, ships = [[5, 10, 20], [2, 20, 30], [3, 15, 25]]
- 通过计算,最大载货量为 100 吨。
总结
本问题通过动态规划方法解决了一个多重背包问题。通过构建一个二维 DP 数组,并逐步更新各个状态,最终得到了预算限制下最大载货量的解。