普通版本:时间:O(N*W);空间:O(N*W)
'''
有n件物品和容量为m的背包 给出i件物品的重量以及价值
求解让装入背包的物品重量不超过背包容量 且价值最大 。
'''
def knapsack(N, W, wts, val):
# dp[i][j]表示当背包容量为j的情况下,在前i个物品中挑选部分放入背包能得到的最大价值
dp = [[0] * (W+1) for _ in range(N+1)]
# n遍历考虑前n个物品是否加入背包中
for n in range(1, N+1):
# w遍历背包的容量
for w in range(1, W+1):
# 背包剩余空间不足
if w - wts[n-1] < 0:
dp[n][w] = dp[n - 1][w]
# I. 当第n个物品不选时,dp值与第n-1次的dp值相同
# II.当第n个物品选择时,dp值为第n-1次中背包剩余容量为【w - wts[n-1]】的dp值
# 加上第n个物品的价值
else:
dp[n][w] = max(dp[n - 1][w - wts[n-1]] + val[n-1], dp[n - 1][w])
return dp[N][W]
空间优化版本:时间:O(N*W);空间:O(W)
使用滚动数组的思想,去掉第一维
def knapsack_space_optimize(N, W, wts, val):
dp = [0] * (W + 1)
for n in range(N):
# 限制下界,防数组越界
# 逆序是防止覆盖n-1次的结果
for w in range(W, wts[n] - 1, -1):
dp[w] = max(dp[w], dp[w-wts[n]] + val[n])
return dp[W]
对于w逆序操作的理解:在考虑第n个物品时, w的逆序遍历过程中,dp[w]表示的是n-1次时dp[n-1][w]的结果,dp[w-wts[n]]表示的是n-1次时dp[n-1][w-wts[n]]的结果.
# 测试用例
input:
N = 3
W = 4
wts = [2, 1, 3]
val = [4, 2, 3]
output:
6
0-1背包类似问题:LeetCode 416.分割等和子集
class Solution(object):
def canPartition(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
sums = sum(nums)
if sums % 2 == 1:
return False
half = sums // 2
dp = [False] * (half + 1)
# 目标为0时,只要一个数字都不取就总是能达到的
dp[0] = True
for i in range(len(nums)):
for j in range(half, 0, -1):
# 防数组越界
if j - nums[i] >= 0:
# 只要有一种情况是可以组成和为half那么就赋值为True,使用or
dp[j] = dp[j] or dp[j - nums[i]]
return dp[half]
完全背包问题:LeetCode 518.零钱兑换II
class Solution(object):
'''
时间:O(N*amount)
空间:O(N*amount)
'''
def change(self, amount, coins):
dp = [[0] * (amount+1) for _ in range(len(coins)+1)]
# 当amount为0时,无为而治,解决方法为1
for i in range(len(coins)+1):
dp[i][0] = 1
for i in range(1, len(coins)+1):
for j in range(1, amount+1):
# 这里的 i-1 非常容易忽略,这里i表示使用第i中硬币,但是对应在数组里的索引是i-1
if j-coins[i-1] >= 0:
# 目标是求硬币所有组合可能情况,所以要加起来
dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]]
else:
dp[i][j] = dp[i-1][j]
return dp[len(coins)][amount]
'''
空间优化:滚动数组
时间:O(N*amount)
空间:O(amount)
'''
def change_optim(self, amount, coins):
dp = [0] * (amount+1)
dp[0] = 1
for i in range(len(coins)):
# 这里不需要逆序
for j in range(1, amount+1):
if j-coins[i] >= 0:
dp[j] = dp[j] + dp[j-coins[i]]
return dp[amount]