83. 小S的货船租赁冒险 | 豆包MarsCode AI刷题

51 阅读3分钟

问题分析与解决思路

在物流运输中,货船的租用是一个关键环节。小S在码头需要租用货船来承载货物,但她希望在有限的预算内,选择能够承载最大货物量的货船组合。这个问题可以归结为经典的“背包问题”变种,即多重背包问题。

问题定义

给定:

  • Q:货船的种类数量。
  • V:总预算(单位:元)。
  • ships:一个列表,其中每个元素是一个元组 [m[i], v[i], w[i]],分别表示第 i 种货船的数量、租赁价格和每艘货船的最大载货量。

目标是:

  • 在预算 V 元内,找到能够承载的最大总货物量。

解决思路

  1. 动态规划初探: 动态规划(Dynamic Programming, DP)是解决此类问题的有效方法。我们可以使用一个数组 dp[j] 来记录预算为 j 时的最大载货量。初始时,dp[0] = 0,表示预算为0时,最大载货量也是0。

  2. 状态转移方程: 对于每一种货船,我们需要考虑在所有可能的预算下,如何更新 dp[j]。假设当前处理到第 i 种货船,其数量为 m[i],租赁价格为 v[i],载货量为 w[i]。我们可以在预算 j 内选择不同数量的第 i 种货船,使得总载货量最大化。

    dp[j] = Math.max(dp[j], dp[j - k * v[i]] + k * w[i]);
    

    其中 k 是从1到 m[i] 的整数,表示选择的第 i 种货船的数量。这个过程确保我们在预算 j 内尝试所有可能的货船组合,选择最优的那一个。

  3. 倒序遍历预算: 由于我们使用的是一维数组,为了避免在更新过程中覆盖前面的状态,我们需要从大到小遍历预算 j。这样可以确保每个预算状态只依赖于之前的预算状态,而不会受到当前货船选择的影响。

代码实现

public static int solution(int Q, int V, List<List<Integer>> ships) {
    int[] dp = new int[V + 1]; // 动态规划数组,dp[j]表示预算为j时的最大载货量
    // 遍历每种货船
    for (int i = 0; i < Q; i++) {
        int m = ships.get(i).get(0); // 第i种货船的数量
        int v = ships.get(i).get(1); // 第i种货船的租赁价格
        int w = ships.get(i).get(2); // 第i种货船的载货量
        // 遍历所有可能的预算
        for (int j = V; j >= v; j--) {
            // 尝试选择不同数量的第i种货船,更新dp[j]
            for (int k = 1; k <= m && k * v <= j; k++) {
                dp[j] = Math.max(dp[j], dp[j - k * v] + k * w);
            }
        }
    }
    return dp[V]; // 返回预算V下的最大载货量
}

复杂度分析

  • 时间复杂度:由于我们需要遍历每种货船的所有可能数量和预算,时间复杂度为 O(Q * V * Σ(m[i])),其中 Σ(m[i]) 是所有种类货船数量之和。
  • 空间复杂度:我们使用了一个大小为 V+1 的数组,因此空间复杂度为 O(V)

优化思路

在实际应用中,如果货船种类数量 Q 或者预算 V 非常大,上述方法可能效率较低。可以考虑的优化方向包括:

  • 二进制分解:将每种货船的数量分解为多个2的幂次,这样可以将 Σ(m[i]) 的复杂度降低到 log(m[i])
  • 单调队列优化:对于某些情况,可以使用单调队列来维护状态转移的最优值,进一步提高效率。