一、01背包问题
题目:假设你是一个小偷,背着一个可以装4磅的背包。你可以盗窃的商品有如下3件,其中每件商品只能选择依次:
商品名称 | 商品价值(美元) | 商品重量(磅) |
---|---|---|
音响 | 3000 | 4 |
笔记本电脑 | 2000 | 3 |
吉他 | 1500 | 1 |
解题思路:
这是一个很典型的动态规划的例子,每个动态规划的算法都从一个网格开始。根据题意可以建立如下网格。
各列为不同的容量(1-4)的背包,各行为可选择的商品
1 | 2 | 3 | 4 | |
---|---|---|---|---|
吉他 | 1500 | 1500 | 1500 | 1500 |
音响 | 1500 | 1500 | 1500 | 3000 |
笔记本电脑 | 1500 | 1500 | 2000 | 3500 |
其中网格填写步骤如下所示:
1)数组设定。设网格为一个二维数组DP[商品种类数][背包容量]。其中DP[i][j]代表前 i 种商品放入容量为 j 背包的最大价值。
2)初始化第一行。即DP[0][0]到DP[0][3]。此时,我们只能选择吉他。那么当背包容量为1到4时,只有一种选择方案,就是吉他。所以第一行所有价值都是吉他的价值1500美元,占用容量1磅。
3)计算后面几行。
DP[i][j] = max(A,B)
A = DP[i-1][j] ;B = 当前商品的价值+剩余空间的价值=当前商品的价值+DP[i-1][j-当前商品的重量]
二、美丽的项链
解题思路:
使用二维数组进行求解。设dp[i][j]代表前i+1种珠宝凑成j颗珠宝的项链。
0 | 1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|---|
第0种宝石 | 1 | 1 | 1 | 1 | 0 | 0 |
第1种宝石 | 1 | 2 | 3 | 4 | 3 | 2 |
第2种宝石 | 1 | 3 | 6 | 10 | 12 | 12 |
如果已知前i种宝石凑成0-j颗珠宝的项链,那么就在此基础上,加上第i+1种珠宝凑成j颗珠宝项链。每种宝石都有最低使用值和最高使用值。此时可以推出一个公式
dp[i][j] = dp[i-1][j-ri]+ dp[i-1][j-ri+1]+ …+dp[i-1][j-li]
如:第一种宝石行,dp[1][3]=df[0][3-0]+dp[0][3-1]]+dp[0][3-2]+dp[0][3-3]=4。即可以表示为,
已知第0种宝石,凑成3颗宝石的方法数,此时第1种宝石不添加,凑成3颗宝石的方法数。
已知第0种宝石,凑成2颗宝石的方法数,此时第1种宝石添加1颗,凑成3颗宝石的方法数。
已知第0种宝石,凑成1颗宝石的方法数,此时第1种宝石添加2颗,凑成3颗宝石的方法数。
已知第0种宝石,凑成0颗宝石的方法数,此时第1种宝石添加3颗,凑成3颗宝石的方法数。
将上面四项相加,即得到dp[1][3]。同理,其他的依照上诉告时进行确定。需要注意的是,添加的宝石有最小和最大的限制。
代码(python3):
#coding:utf-8
n,m = map(int,input().split())
L=[]
R=[]
for i in range(n):
li,ri = map(int,input().split())
L.append(li)
R.append(ri)
dp = [[0 for i in range(m+1)] for j in range(n)]
#初始化第一行
for i in range(L[0],R[0]+1):
dp[0][i] = 1
#剩余其他行使用公式进行计算
for i in range(1,n):
for j in range(m+1):
left = max(0,j-R[i])
right = max(0,j-L[i])
for l in range(left,right+1):
dp[i][j] += dp[i-1][l]
print(dp)
print(dp[n-1][m])