问题描述
有n件物品,有容量为c的背包
每件物品占的容量为\(\omega_{i}\) ;价值为\(v_i\)
问在容量限制下怎样选择才能带走最大价值的物品
根据物品的选择,还可以分为以下几种类型:
1.0-1背包问题
2.完全背包问题
3.多重背包问题
4.混合背包问题
一、.0-1背包问题
基础递归解决
时间复杂度为 O(2ⁿ)
每个物品选择或不选择,返回两个选择中最大价值
def ks_recursion(n , c , weights , values):if n == 0 or capacity == 0:return 0if weights[n-1] > capacity:return knapsack_recursive(weights, values, capacity, n-1)not_take = knapsack_recursive(weights, values, capacity, n-1)take = knapsack_recursive(weights, values, capacity - weights[n-1], n-1) + values[n-1]return max(not_take, take)
记忆递归
通过缓存中间结果,将时间复杂度优化为 O(n*c)
字典记录已经存在的计算结果,避免重复计算同一子问题
def ks_recursion_memory(n,c,w:list,v:list,memo:dict):if n <0 or c <=0:#return 0if (n,c) in memo:return memo[(n,c)]no_taken = ks_loop(n-1,c,w,v,memo)#take = 0#if c >= w[n]:take = ks_loop(n-1,c-w[n],w,v,memo) + v[n]#memo[(n,c)] = max(take,no_taken)return memo[(n,c)]
二维数组
复杂度:O(n * c)
对每个物品 i 和每个容量 w:
若当前物品重量 > 剩余容量:无法选择该物品,继承前 i-1 个物品的结果。
dp[i][w] = dp[i-1][w]
若当前物品重量 ≤ 剩余容量:决策选或不选该物品,取最大价值。
dp[i][w] = max(dp[i-1][w], dp[i-1][w - weights[i-1]] + values[i-1]
)
最终结果在dp[n][c]
def ks_double_dim(n, c, w, v):dp = [[0] * (c + 1) for _ in range(n + 1)]for i in range(1, n + 1): # i:每个物品for ca in range(1, c + 1): # c:每个容量if w[i - 1] > ca:dp[i][ca] = dp[i - 1][ca]else:dp[i][ca] = max(dp[i - 1][ca], dp[i - 1][ca - w[i - 1]] + v[i - 1])##return dp[n][c]
一维数组
def ks_one(n,c,w,v):dp = [0]*(c+1)for i in range(n):for ca in range(c,w[i]-1,-1):dp[ca] = max(dp[ca],dp[ca-w[i]]+v[i])return dp[c]