Hard草稿★★★★★O(plans * n * minProfit) 時間 · O(n * minProfit) 空間
Dynamic ProgrammingKnapsack
Patterns🎒 背包🔲 網格・雙序列 DP
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

879Profitable Schemes2-D DPHard2-D DP

有一個犯罪集團,共 n 個成員。有多個犯罪計劃,第 i 個計劃需要 group[i] 個成員,能獲利 profit[i]。同一個成員不能參與多個計劃。回傳能獲利至少 minProfit 的方案數(對 10^9+7 取餘)。

Example:

Input: n = 5, minProfit = 3, group = [2,2], profit = [2,3] Output: 2

Intuition

TIP

三維 0/1 背包:維度為「計劃」、「人數」、「利潤」,計算滿足最低利潤的方案數。

Approaches

1. 3D DP — O(plans * n * minProfit) / O(plans * n * minProfit)
  • Idea: dp[i][j][k] 表示考慮前 i 個計劃、用 j 個人、獲利為 k 的方案數。
  • Time: O(plans * n * minProfit)
  • Space: O(plans * n * minProfit)
class Solution {
    fun profitableSchemes(n: Int, minProfit: Int, group: IntArray, profit: IntArray): Int {
        val MOD = 1_000_000_007L
        val plans = group.size
        val dp = Array(plans + 1) { Array(n + 1) { LongArray(minProfit + 1) } }
        for (j in 0..n) dp[0][j][0] = 1

        for (i in 1..plans) {
            val g = group[i - 1]
            val p = profit[i - 1]
            for (j in 0..n) {
                for (k in 0..minProfit) {
                    dp[i][j][k] = dp[i - 1][j][k]
                    if (j >= g) {
                        val prevK = maxOf(0, k - p)
                        dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - g][prevK]) % MOD
                    }
                }
            }
        }

        var result = 0L
        for (j in 0..n) result = (result + dp[plans][j][minProfit]) % MOD
        return result.toInt()
    }
}
⭐ 2. 2D DP (Space Optimized) — O(plans * n * minProfit) / O(n * minProfit)
  • Idea: 壓縮掉計劃維度,逆序遍歷人數和利潤。利潤超過 minProfit 的統一歸到 minProfit
  • Time: O(plans * n * minProfit)
  • Space: O(n * minProfit)
class Solution {
    fun profitableSchemes(n: Int, minProfit: Int, group: IntArray, profit: IntArray): Int {
        val MOD = 1_000_000_007L
        val dp = Array(n + 1) { LongArray(minProfit + 1) }
        for (j in 0..n) dp[j][0] = 1

        for (i in group.indices) {
            val g = group[i]
            val p = profit[i]
            for (j in n downTo g) {
                for (k in minProfit downTo 0) {
                    val newK = minOf(k + p, minProfit)
                    dp[j][newK] = (dp[j][newK] + dp[j - g][k]) % MOD
                }
            }
        }
        return dp[n][minProfit].toInt()
    }
}

🔑 Takeaways