解題說明
Profitable Schemes
題目描述:
有 n 名成員可以參與犯罪計畫。給定整數 minProfit,以及兩個陣列 group 和 profit,其中第 i 個計畫需要 group[i] 名成員參與,能產生 profit[i] 的利潤。每名成員最多參與一個計畫。求在總利潤至少為 minProfit 的前提下,有多少種選擇計畫的方案(結果取模 10^9 + 7)。
解題思路:
這是一道三維 0/1 背包問題。定義 dp[i][j][k] 為考慮前 i 個計畫、使用最多 j 名成員、利潤至少為 k 的方案數。
為了節省空間,我們可以使用二維 DP:dp[j][k] 表示使用恰好 j 名成員、利潤為 k 的方案數。遍歷每個計畫時反向更新(類似 0/1 背包)。
關鍵技巧:利潤維度的上界設為 minProfit,當利潤超過 minProfit 時統一歸入 minProfit 這一格,因為「超過」和「剛好等於」對結果的貢獻相同。
最終答案為 sum(dp[j][minProfit]) 對所有 j 從 0 到 n。
C++ 解法
複雜度分析
時間複雜度:O(len * n * minProfit) — 其中 len 是計畫數量,每個計畫遍歷所有 (成員數, 利潤) 狀態。
空間複雜度:O(n * minProfit) — 二維 DP 陣列。
虛擬碼
1. Initialize dp[n+1][minProfit+1] with all zeros; set dp[0][0] = 1.
2. For each crime i with group[i] members and profit[i] profit:
a. For j from n down to group[i]:
For k from minProfit down to 0:
newProfit = min(k + profit[i], minProfit)
dp[j][newProfit] += dp[j - group[i]][k]
dp[j][newProfit] %= MOD
3. Sum dp[j][minProfit] for all j from 0 to n.
4. Return the sum modulo 10^9 + 7.其他解法
三維 DP(未壓縮空間):O(len * n * minProfit) 時間、O(len * n * minProfit) 空間。直接定義 dp[i][j][k] 為前 i 個計畫、j 名成員、利潤 k 的方案數。邏輯更直觀但空間消耗大。
記憶化搜尋:同樣的狀態定義,用遞迴 + memo 實現。適合不熟悉迭代 DP 方向的人,但遞迴開銷略高。
延伸思考
- 為什麼利潤維度可以在
minProfit處「封頂」?如果不封頂會如何影響時間複雜度? - 本題與經典 0/1 背包的差異在哪裡?為什麼需要兩個「容量」維度?
- 如果每個計畫可以重複選擇(完全背包),DP 的遍歷方向需要如何改變?