写在前面
- 这是我对动态规划一些入门题的笔记,主要便于随时随地的回顾这些基础内容。
- 基本都是些简单题目,因为复杂的题目代码太长,不便于作为笔记进行重温。
- 前续:[算法][动态规划]动态转移过程与Python实现小样两例(切绳子与跳台阶)
- 背包问题讲义
如果无法访问码云的代码库,请访问此处:本文源码@Gist
题目描述
有一个重量上限容量为 t o t a l total total 的背包和 i t e m _ n u m item\_num item_num 种物品,第 i i i 种物品的重量是 w i w_i wi 、价值为 v i v_i vi ,求问选择将哪些物品放入背包(每种物品的数量有且只有一件)可以得到最大的价值。
问题求解
1.1 动态过程
动态规划的精髓在于拆解大问题为子问题。我们给定一种数据结构dp[item_num][total]
,其中,dp[i][j]
表示第i+1
次尝试把物品i
放入背包后,背包剩余容量为j
,此时背包中的物品总价值为dp[i][j]
。
Tip:物品的序数从
0
计起,放入背包的动作过程从第1
次计起。
由于每次尝试把物品放入背包的过程是一个动态过程,其动态转移方程为:
d p [ i + 1 ] [ j ] = m a x { d p [ i ] [ j − w i ] + v i , d p [ i ] [ j ] } dp[i+1][j]=max\{dp[i][j-w_i]+v_i,dp[i][j]\} dp[i+1][j]=max{
dp[i][j−wi]+vi,dp[i][j]}显然i
是主要的状态转移量:
for i in range(item_num): # 第i+1次尝试放入第i个物品(物品序号从0起||显然,尝试次数从1起,即i+1)
# 以下是动态转移过程
for j in range(total, -1, -1): # 倒序遍历背包剩余容量,即 for(inr j = total; j>=0; j--)
dp[i+1][j] = max(dp[i][j-w[i]]+v[i],dp[i][j]) if j >= w[i] else dp[i][j]
# 等价于:
# if j >= w[i] and dp[i][j-w[i]] + v[i] > dp[i][j]: # 第[i+1]次尝试放入时选择放入第[i]个物品
# dp[i+1][j] = dp[i][j-w[i]] + v[i] # 更新第i+1次尝试放入的推测值(物品总价值)
# else: # 跳过物品,不考虑放入该物品,拷贝"前[i]次尝试放入"的堆栈数据到[i+1]上
# dp[i+1][j] = dp[i][j]
即,第
i+1
次考虑要不要把第i
个物品放入背包(0-1问题中恰好 n n n个物品最多只考虑 n n n次"放不放"的问题),知道它的重量 w i w_i wi 和价值 v i v_i vi,当dp[i][j-w[i]] + v[i] > dp[i][j]
,即前一次(dp[i][~]
)考虑时,没有放入 w i w_i wi重量的