題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。
Problem Statement
題目簡述
有 N N N 個物品,每個物品重 w i w_i w i 價值 v i v_i v i 。求在總重量不超過 W W W 的限制下,能獲得的最大總價值。
Constraints
約束條件
1 ≤ N ≤ 100 1 \le N \le 100 1 ≤ N ≤ 1 0 0
1 ≤ W ≤ 1 0 9 1 \le W \le 10^9 1 ≤ W ≤ 1 0 9
1 ≤ w i ≤ W 1 \le w_i \le W 1 ≤ w i ≤ W
1 ≤ v i ≤ 1 0 3 1 \le v_i \le 10^3 1 ≤ v i ≤ 1 0 3
思路:轉換狀態定義
標準 0/1 背包的複雜度為 O ( N W ) O(NW) O ( N W ) ,但本題 W W W 高達 1 0 9 10^9 1 0 9 ,直接套用會超時且記憶體不足。
題目給出 N ≤ 100 N \le 100 N ≤ 1 0 0 且 v i ≤ 1 0 3 v_i \le 10^3 v i ≤ 1 0 3 ,因此總價值上界 僅為 V max = N ⋅ max ( v i ) = 1 0 5 V_{\max} = N \cdot \max(v_i) = 10^5 V m a x = N ⋅ max ( v i ) = 1 0 5 。
這啟發我們交換 DP 的狀態與目標 :
標準背包
本題做法
dp[w] = max_value
dp[v] = min_weight
固定重量,求最大價值
固定價值,求最小重量
DP 定義
f[v]:恰好達成總價值 v v v 所需的最小總重量 。
對於每個物品 ( w i , v i ) (w_i, v_i) ( w i , v i ) ,從大到小枚舉價值 j j j :
f [ j ] = min ( f [ j ] , f [ j − v i ] + w i ) f[j] = \min\bigl(f[j],\; f[j - v_i] + w_i\bigr)
f [ j ] = min ( f [ j ] , f [ j − v i ] + w i )
初始化 :f [ 0 ] = 0 f[0] = 0 f [ 0 ] = 0 ,其餘為 ∞ \infty ∞ 。
答案 :滿足 f [ v ] ≤ W f[v] \le W f [ v ] ≤ W 的最大 v v v 。
複雜度分析
時間複雜度:O ( N ⋅ V max ) \mathcal{O}(N \cdot V_{\max}) O ( N ⋅ V m a x ) ,約 1 0 7 10^7 1 0 7 次運算。
空間複雜度:O ( V max ) \mathcal{O}(V_{\max}) O ( V m a x ) ,約 1 0 5 10^5 1 0 5 大小的陣列。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def solve (): N, W = map (int , input ().split()) items = [tuple (map (int , input ().split())) for _ in range (N)] V = sum (v for _, v in items) f = [float ("inf" )] * (V + 1 ) f[0 ] = 0 for w, v in items: for j in range (V, v - 1 , -1 ): f[j] = min (f[j], f[j - v] + w) print (max (i for i, w in enumerate (f) if w <= W)) if __name__ == "__main__" : solve()