小S的货船租赁冒险

51 阅读5分钟

问题分析

本问题可以看作一个典型的背包问题,但是与常见的 0/1 背包或完全背包不同的是,这里每种类型的货船都有数量限制。因此,我们需要使用动态规划来求解这个问题。任务是根据预算,在不同的货船类型中选择,达到最大载货量。

思路解析

  1. 动态规划:我们可以使用动态规划来解决背包问题。设 dp[i][j] 表示在前 i 种货船中,预算为 j 时所能达到的最大载货量。

  2. 状态转移

    • 对于每种类型的货船(设它的数量是 m[i],租赁价格是 v[i],最大载货量是 w[i]),我们可以选择不租用该类型货船,或者租用一定数量的该类型货船。对于每种情况,我们需要计算能承载的最大货物量。
  3. 完全背包的扩展:当有多种货船时,可以通过迭代不同数量的货船来处理。由于每种货船的数量是有限的,这就涉及到多重背包问题,可以通过转化为完全背包的问题来处理。

  4. 递推关系

    • 对于每个状态 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);
    }
}

代码详解

  1. 输入参数处理

    • Q 是货船的种类数。
    • V 是可用的总预算。
    • ships 是一个列表,包含每种货船的数量、租赁价格和最大载货量。
  2. 动态规划数组 dp

    • dp[i][j] 表示前 i 种货船,且预算为 j 时的最大载货量。
    • 初始化:dp[0][j] = 0,即没有货船时载货量为0。
  3. 遍历所有货船类型:对每种货船类型进行遍历。

    • 对每种货船类型,我们通过 k 来表示租用 k 艘该类型货船的情况,更新 dp 数组。
  4. 时间复杂度

    • 假设有 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 数组,并逐步更新各个状态,最终得到了预算限制下最大载货量的解。