1、01背包问题
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。输出选了哪些物品。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
题解
一、dp矩阵,横轴代表背包的容量,从0开始到V。纵轴代表物品数量,0代表前0个物品,1代表前1个物品,以此类推。为了方便理解,加上第0个物品,体积为0,价值为0。
二、dp[i][j]代表,当前容量是j,当前物品编号为i。
三、如果放不下物品i,那么前i个物品的最佳组合和前i-1个物品的最佳组合是一样的。即
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
dp[i][j]=dp[i-1][j]
dp[i][j]=dp[i−1][j]
四、如果放得下物品i。则有两种情况,放物品i和不放物品i。如果选择不放物品i,那么和(三)中情况一样,
t
e
m
p
1
=
d
p
[
i
−
1
]
[
j
]
temp1 = dp[i-1][j]
temp1=dp[i−1][j]。如果选择放物品i,那么,首先计算当前容量j-物品i占据的空间剩余的空间left_c,这时最大价值为物品j的价值+剩余空间left_c所能达到的最大价值。由于每个物品只有一个,则剩余空间left_c的最大价值等于前i-1个物品时,剩余空间left_c的最大价值。即
t
e
m
p
2
=
v
[
j
]
+
d
p
[
i
−
1
]
[
l
e
f
t
_
c
]
temp2 = v[j]+dp[i-1][left\_c]
temp2=v[j]+dp[i−1][left_c]。
五、比较不放物品i的价值temp1和放物品i的价值temp2。选择价值大的作为结果。
样例
w = [1,2,3,4]
v = [2,4,4,5]
物品编号 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 2 | 2 | 2 | 2 |
2 | 0 | 2 | 4 | 6 | 6 | 6 |
3 | 0 | 2 | 4 | 6 | 6 | 6 |
4 | 0 | 2 | 4 | 6 | 6 | 8 |
判断哪些物品被选取了:
一、从表的右下角开始回溯。
二、从上面的题解中可以看出,如果前i个物品的最佳组合的价值和前i-1个物品最佳组合的价值一样,说明第i个物品没有被选择。否则,第i个物品被选择。
三、第i个物品被选择了之后,当前容量减去该物品体积得到剩余体积c,接下来选择的物品只能是比剩余体积c小的物品。则接着从剩余体积c,的前i-1个物品的表右下角开始回溯。
def backpack01(w,v,c): #0,1 背包问题
w = [0]+w #第0个物品的体积
v = [0]+v #第0个物品的价值
n = len(w)
dp =[[0]*(c+1) for _ in range(n)]
for i in range(1,n):
for j in range(1,c+1):
temp_c= j #当前容量
temp_w = w[i] #这个物体的体积
temp_v = v[i]
if temp_c < temp_w: #当前容量不能放下这个物体
dp[i][j]=dp[i-1][j]
else: #当前容量能装下这个物体
#如果不装,则当前最大容量和
temp1 = dp[i-1][j]
#如果装,这当前价值为该物品价值与减去该物体体积后最大价值
left_c = temp_c-temp_w
temp2 = temp_v + dp[i-1][left_c]
dp[i][j]=max(temp1,temp2)
print(dp)
max_value = dp[-1][-1] #最大价值
print(max_value)
ans = []
for i in range(4,0,-1): #注意,第0个物品其实是没有的,所以这里不用考虑第0个物品
if dp[i][c] != dp[i-1][c]:
ans.append(i)
c = c - w[i-1]
print(ans) #包里选了哪些物品
N,V = map(int,input().split())
print(N,V)
w = []
v = []
for i in range(N):
t_w,t_v = list(map(int,input().split()))
w.append(t_w)
v.append(t_v)
backpack01(w,v,V)
2、完全背包问题
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
题解
这题和上一题类似。不同的是,每个物品可以被选择无数次。则第(四)会略有不同。
如果能够放下放物品i,那么同样也会有两种情况,选择放物品i和不放物品i。选择不放物品i时,和前面一样,
t
e
m
p
1
=
d
p
[
i
−
1
]
[
j
]
temp1 = dp[i-1][j]
temp1=dp[i−1][j]。如果选择放物品i,首先计算放置了1个物品i之后剩下的容量left_c,这时最大价值为物品j的价值+剩余空间left_c所能达到的最大价值。由于物品i可以有无数个,则剩余空间left_c的最大价值等于前i个物品时,剩余空间left_c的最大价值。即
t
e
m
p
2
=
v
[
j
]
+
d
p
[
i
]
[
l
e
f
t
_
c
]
temp2 = v[j]+dp[i][left\_c]
temp2=v[j]+dp[i][left_c]。
样例
w = [1,2,3,4]
v = [2,4,4,5]
物品编号 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 4 | 6 | 8 | 10 |
2 | 0 | 2 | 4 | 6 | 8 | 10 |
3 | 0 | 2 | 4 | 6 | 8 | 10 |
4 | 0 | 2 | 4 | 6 | 8 | 10 |
def full_backpack(w,v,c): # 完全背包问题
w = [0] + w # 第0个物品的体积
v = [0] + v # 第0个物品的价值
n = len(w)
dp =[[0]*(c+1) for _ in range(n)]
# print(dp)
for i in range(1,n):
for j in range(1,c+1):
temp_c= j #当前容量
temp_w = w[i] #这个物体的体积
temp_v = v[i]
if temp_c < temp_w: #当前容量不能放下这个物体
dp[i][j]=dp[i-1][j]
else: #当前容量能装下这个物体
# 如果不装,则当前最大容量和
temp1 = dp[i - 1][j]
# 如果装,这当前价值为该物品价值与减去该物体体积后最大价值
left_c = temp_c - temp_w
temp2 = temp_v + dp[i][left_c]
dp[i][j] = max(temp1, temp2)
print(dp[-1][-1])
3、多重背包问题
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
题解
把问题当做01背包问题来解。
样例:
w = [1,2,3,4]
v = [2,4,4,5]
s = [3,1,3,2]
相当于:w = [1,2,3,4,1,1,3,3,4]
v = [2,4,4,5,2,2,4,4,5]
物品编号 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 2 | 2 | 2 | 2 |
2 | 0 | 2 | 4 | 6 | 6 | 6 |
3 | 0 | 2 | 4 | 6 | 6 | 8 |
4 | 0 | 2 | 4 | 6 | 6 | 8 |
5 | 0 | 2 | 4 | 6 | 8 | 8 |
6 | 0 | 2 | 4 | 6 | 8 | 10 |
7 | 0 | 2 | 4 | 6 | 8 | 10 |
8 | 0 | 2 | 4 | 6 | 8 | 10 |
9 | 0 | 2 | 4 | 6 | 8 | 10 |
n = len(w)
for i in range(n):
si = s[i]
while si-1 >0:
si -= 1
w.append(w[i])
v.append(v[i]) #暴力当做01背包问题来解
w = [0] + w # 第0个物品的体积
v = [0] + v # 第0个物品的价值
n = len(w)
print(w,v) #相当于背包问题的输入了
dp = [[0] * (c + 1) for _ in range(n)]
for i in range(1,n):
for j in range(1,c+1):
temp_c= j #当前容量
temp_w = w[i] #这个物体的体积
temp_v = v[i]
if temp_c < temp_w: #当前容量不能放下这个物体
dp[i][j]=dp[i-1][j]
else: #当前容量能装下这个物体
# 如果不装,则当前最大容量和
temp1 = dp[i - 1][j]
# 如果装,这当前价值为该物品价值与减去该物体体积后最大价值
left_c = temp_c - temp_w
temp2 = temp_v + dp[i - 1][left_c]
dp[i][j] = max(temp1, temp2)
print(dp[-1][-1])
4、多重背包问题II
题目描述同上,但是输入样例的s的范围从0到2000。所以不能直接用上面的方法改成01背包问题。这里用到二进制优化的方法。
题解
引入:数字7,最少能用几个数字表示
考虑到二进制, 2 0 = 1 , 2 1 = 2 , 2 2 = 4 2^0=1,2^1=2,2^2=4 20=1,21=2,22=4
三个数字组合就能组成7:0=0; 1=1; 2=2; 3=1+2; 4=4; 5=1+4; 6=2+4; 7=1+2+4
当不能被整加表示时,例如数字10,则只需要用10-1-2-4=3,则用1,2,4,3这几个数字表示即可。8=1+3+4; 9=1+4+4; 10=2+4+4
则有当一个物体的数量为si时,可以将物体合并当做一个物体。例如这个物体数量为5,价值为2,体积为3。则可以拆分合并为:1个物体(w,v)=(2,3) + 2个物体:(22,32)=(4,6) + 2个物体:(4,6)。最后变成了3个物体,每个物体只有一个。
然后这个问题就转成了01背包问题。注意吧最后所有的物体按照容量排序后再按01背包问题解答。
样例
w = [1,2,3,4]
v = [2,4,4,5]
s = [3,1,3,2]
则相当于是(wi,vi):[(1,2),(2,4),(2,4),(3,4),(6,8),(4,5),(4,5)]
按照物体的体积排序后为:[(1,2),(2,4),(2,4),(3,4),(4,5),(4,5),(6,8)]
物品编号 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 2 | 2 | 2 | 2 |
2 | 0 | 2 | 4 | 6 | 6 | 6 |
3 | 0 | 2 | 4 | 6 | 8 | 10 |
4 | 0 | 2 | 4 | 6 | 8 | 10 |
5 | 0 | 2 | 4 | 6 | 8 | 10 |
6 | 0 | 2 | 4 | 6 | 8 | 10 |
7 | 0 | 2 | 4 | 6 | 8 | 10 |
def multi_backpack2(w,v,s,c):
n = len(w)
new_wv = []
for i in range(n):
si = s[i]
step = 1/2
for j in range(1,si,int(step*2)):
si -= j
new_wv.append((w[i]*j,v[i]*j))
if si != 0:
new_wv.append((w[i] * si, v[i] * si))
# print(new_wv) #[(1, 2), (2, 4), (2, 4), (3, 4), (6, 8), (4, 5), (4, 5)]
new_wv.sort()
# print(new_wv) #[(1, 2), (2, 4), (2, 4), (3, 4), (4, 5), (4, 5), (6, 8)]
#转换成 01 背包问题
new_wv = [(0,0)] + new_wv
print(new_wv)
n = len(new_wv)
dp = [[0] * (c + 1) for _ in range(n)]
# print(dp)
for i in range(1, n):
for j in range(1, c + 1):
temp_c = j # 当前容量
temp_w = new_wv[i][0] # 这个物体的体积
temp_v = new_wv[i][1]
if temp_c < temp_w: # 当前容量不能放下这个物体
dp[i][j] = dp[i - 1][j]
else: # 当前容量能装下这个物体
# 如果不装,则当前最大容量和
temp1 = dp[i - 1][j]
# 如果装,这当前价值为该物品价值与减去该物体体积后最大价值
left_c = temp_c - temp_w
temp2 = temp_v + dp[i - 1][left_c]
dp[i][j] = max(temp1, temp2)
print(dp[-1][-1])
5、混合背包问题
有 N 种物品和一个容量是 V 的背包。
物品一共有三类:
第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si 次(多重背包);
每种体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
si=−1 表示第 i 种物品只能用1次;
si=0 表示第 i 种物品可以用无限次;
si>0 表示第 i 种物品可以使用 si 次;
输出格式
输出一个整数,表示最大价值。
题解
就等于把前面的3种情况合并。把多重背包问题拆解成二进制的01背包问题。所以就相当于是01背包问题和完全背包问题。不同情况用不同的状态转移方程即可。
样例
w = [1,2,3,4]
v = [2,4,4,5]
s = [-1,1,0,2]
则相当于是(si,wi,vi):[(-1, 1, 2), (1, 2, 4), (0, 3, 4), (1, 4, 5), (1, 4, 5)]
物品编号 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 2 | 2 | 2 | 2 |
2 | 0 | 2 | 4 | 6 | 6 | 6 |
3 | 0 | 2 | 4 | 6 | 6 | 8 |
4 | 0 | 2 | 4 | 6 | 6 | 8 |
5 | 0 | 2 | 4 | 6 | 6 | 8 |
def hunhe(w,v,s,c):
n = len(w)
new_wv = []
for i in range(n):
si = s[i]
if si < 0: #01背包
new_wv.append((-1,w[i],v[i]))
elif si == 0: # 完全背包
new_wv.append((0,w[i],v[i]))
else: #多重背包,拆解
step = 1 / 2
for j in range(1, si, int(step * 2)):
si -= j
new_wv.append((j,w[i] * j, v[i] * j))
if si != 0:
new_wv.append((si,w[i] * si, v[i] * si))
new_wv = [(0, 0,0)] + new_wv
print(new_wv)
n = len(new_wv)
dp = [[0] * (c + 1) for _ in range(n)]
for i in range(1, n):
for j in range(1, c + 1):
temp_c = j # 当前容量
temp_w = new_wv[i][1] # 这个物体的体积
temp_v = new_wv[i][2]
if temp_c < temp_w: # 当前容量不能放下这个物体
dp[i][j] = dp[i - 1][j]
else: # 当前容量能装下这个物体
# 如果不装,则当前最大容量和
temp1 = dp[i - 1][j]
# 如果装,这当前价值为该物品价值与减去该物体体积后最大价值
left_c = temp_c - temp_w
if new_wv[i][0] == 0: #完全背包问题
temp2 = temp_v + dp[i][left_c]
else: #01 背包问题
temp2 = temp_v + dp[i - 1][left_c]
dp[i][j] = max(temp1, temp2)
print(dp[-1][-1])
6、二维费用背包问题
有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。
每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,M,用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。
接下来有 N 行,每行三个整数 vi,mi,wi,用空格隔开,分别表示第 i 件物品的体积、重量和价值。
输出格式
输出一个整数,表示最大价值。
题解
二维费用同一维费用的思想一致,约束条件从一个变成两个而已。约束条件不仅要看当前容量能不能装下当前物体,也要看当前最大承重能不能装下当前物体。
此时的dp矩阵应该是一个三维的矩阵,维度为(N+1)×(V+1)×(M+1)。代表前i个物体时,体积为vi,最大承重为mi时最大价值。由于前i个物体的情况只与前i-1个物体的情况有关,所以无需记录整个3维矩阵,每次只需保留第i-1个物体时的dp[i-1]矩阵即可。
一、dp[i]矩阵,横轴代表背包的容量,从0开始到V。纵轴代表背包最大承重,从0开始到M,i代表前i个物品,为了方便理解,加上第0个物品,体积为0,价值为0。每次都要前i个物体,然后遍历从0到V(vi),从0到M(mi)的最大价值。
二、记录前i-1时的dp矩阵,存放为temp。
三、如果放不下物品i,那么前i个物品的最佳组合和前i-1个物品的最佳组合是一样的。即
d
p
[
m
i
]
[
v
i
]
=
t
e
m
p
[
m
i
]
[
v
i
]
dp[mi][vi]=temp[mi][vi]
dp[mi][vi]=temp[mi][vi]
四、如果放得下物品i。则有两种情况,放物品i和不放物品i。如果选择不放物品i,那么和(三)中情况一样,
t
e
m
p
_
n
o
t
_
a
d
d
=
t
e
m
p
[
i
m
i
]
[
v
i
]
temp\_not\_add=temp[imi][vi]
temp_not_add=temp[imi][vi]。如果选择放物品i,那么,首先计算当前容量j-物品i占据的空间剩余的空间left_v,以及当前最大承重-物体i的重量的剩余最大承重left_m,这时最大价值为物品j的价值+剩余空间left_v,最大承重为left_m时所能达到的最大价值。由于每个物品只有一个,则剩余空间left_v,最大承重为left_m时的最大价值等于前i-1个物品时,剩余空间left_v,最大承重为left_m时的最大价值。即
t
e
m
p
_
a
d
d
=
w
[
i
]
+
t
e
m
p
[
l
e
f
t
_
v
]
[
l
e
f
t
_
m
]
temp\_add = w[i]+temp[left\_v][left\_m]
temp_add=w[i]+temp[left_v][left_m]。
五、比较不放物品i的价值temp_not_add和放物品i的价值temp_add。选择价值大的作为结果。
样例
N=4,V=5,M=6
v = [1,2,3,4]
m = [2,4,4,5]
w = [3,4,5,6]
当前0个物体时,无论体积容量多大,最大价值都是0
最大承重 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
6 | 0 | 0 | 0 | 0 | 0 | 0 |
当前1个物体时:
最大承重 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 3 | 3 | 3 | 3 | 3 |
3 | 0 | 3 | 3 | 3 | 3 | 3 |
4 | 0 | 3 | 3 | 3 | 3 | 3 |
5 | 0 | 3 | 3 | 3 | 3 | 3 |
6 | 0 | 3 | 3 | 3 | 3 | 3 |
当前2个物体时:
最大承重 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 3 | 3 | 3 | 3 | 3 |
3 | 0 | 3 | 3 | 3 | 3 | 3 |
4 | 0 | 3 | 4 | 4 | 4 | 4 |
5 | 0 | 3 | 4 | 4 | 4 | 4 |
6 | 0 | 3 | 4 | 7 | 7 | 7 |
当前3个物体时:
最大承重 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 3 | 3 | 3 | 3 | 3 |
3 | 0 | 3 | 3 | 3 | 3 | 3 |
4 | 0 | 3 | 4 | 5 | 5 | 5 |
5 | 0 | 3 | 4 | 5 | 5 | 5 |
6 | 0 | 3 | 4 | 7 | 8 | 8 |
当前4个物体时:
最大承重 \ 背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 3 | 3 | 3 | 3 | 3 |
3 | 0 | 3 | 3 | 3 | 3 | 3 |
4 | 0 | 3 | 4 | 5 | 5 | 5 |
5 | 0 | 3 | 4 | 5 | 6 | 6 |
6 | 0 | 3 | 4 | 7 | 8 | 8 |
def backpack2(v,m,w,V,M):
# v 代表体积
# m 代表重量
# w 代表价值
n = len(v)
dp = [[0]*(V+1) for _ in range(M+1)]
v = [0] + v
m = [0] + m
w = [0] + w
for i in range(1,n+1):
temp = copy.deepcopy(dp) #第i个物体的情况只与第i-1个物体情况有关,所以前面i-2个物体的dp不需要记录了
for mi in range(1,M+1):
for vi in range(1,V+1):
#如果装不下第i个物体
if not (v[i]<=vi and m[i] <= mi):
dp[mi][vi] = temp[mi][vi]
#如果装得下第i个物体
else:
#可以选择装
left_v = vi - v[i]
left_m = mi - m[i]
temp_add = w[i] + temp[left_m][left_v]
#或者选择不装
temp_not_add = temp[mi][vi] #同上面装不下时的结果是一样的
dp[mi][vi] = max(temp_add,temp_not_add)
print(dp)
print('=======================')
print(dp[-1][-1])
7、分组背包问题
有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。
接下来有 N 组数据:
每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
输出格式
输出一个整数,表示最大价值。
题解
同01背包问题一样,因为组内只能选取一个物体,所以前i个物体的问题变成前i组的最大价值问题。前i组最大价值只与前i-1组最大值有关,所以同上一题类似,只需要记录前i-1组时的最大价值的dp数组。
所不同的是,当是第i组物体时,遍历组内所有的物体(总物体个数为m),选取当前容量j下,能够放下的最大的价值的物体。如果当前容量下,第i组内任意一个物体都放不下,则
d
p
[
j
]
=
d
p
_
o
l
d
[
j
]
dp[j]=dp\_old[j]
dp[j]=dp_old[j]。如果当前容量下,第i组内很多个物体都能够满足,则遍历该组内所有的物体,每一个能放下的物体都放置一次。
假设第k个物体满足,可以选择添加该物体,则该物体的价值为
g
o
o
d
[
i
]
[
k
]
[
1
]
good[i][k][1]
good[i][k][1]。当前物体的体积为
g
o
o
d
s
[
i
]
[
k
]
[
0
]
goods[i][k][0]
goods[i][k][0],剩余体积为
l
e
f
t
_
v
=
j
−
g
o
o
d
s
[
i
]
[
k
]
[
0
]
left\_v = j-goods[i][k][0]
left_v=j−goods[i][k][0],当前容量当前物体的价值为
t
e
m
p
_
a
d
d
=
g
o
o
d
[
i
]
[
k
]
[
1
]
+
d
p
_
o
l
d
[
l
e
f
t
_
v
]
temp\_add =good[i][k][1] + dp\_old[left\_v]
temp_add=good[i][k][1]+dp_old[left_v]。
也可以选择不添加该物体,
t
e
m
p
_
n
o
t
_
a
d
d
=
d
p
_
o
l
d
[
j
]
temp\_not\_add = dp\_old[j]
temp_not_add=dp_old[j]。
比较添加该物体,不添加该物体,或者添加组内其他物体时的价值,选取最大的价值。
d
p
[
j
]
=
m
a
x
(
t
e
m
p
_
a
d
d
,
t
e
m
p
_
n
o
t
_
a
d
d
,
d
p
[
j
]
)
dp[j] = max(temp\_add,temp\_not\_add,dp[j])
dp[j]=max(temp_add,temp_not_add,dp[j])。
如果组内第k个物体不能满足当前容量,则当前最大价值为组内放了前k-1个的最大价值与前i组的最大价值比较,选择大的做为当前最大价值。
d
p
[
j
]
=
m
a
x
(
d
p
[
j
]
,
d
p
_
o
l
d
[
j
]
)
dp[j] = max(dp[j],dp\_old[j])
dp[j]=max(dp[j],dp_old[j])
样例
N=3,V=5
2
1 2
2 4
1
3 4
1
4 5
背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 4 | 4 | 4 | 4 |
2 | 0 | 2 | 4 | 4 | 6 | 8 |
3 | 0 | 2 | 4 | 4 | 6 | 8 |
def group_backpack(N,V):
goods = [] #存放所有的物体
for ni in range(N): #N组物体
n = int(input()) #第ni组物体里面有n个物体
temp = []
for i in range(n):
vi,wi = list(map(int,input().split()))
temp.append((vi,wi))
temp.sort() #将组内的物体按照体积的大小排序
goods.append(temp)
dp = [0]*(V+1) #前0组物体
for i in range(N): #前i组物体
dp_old = copy.deepcopy(dp)
dp = [0] * (V+1)
for j in range(1,V+1): #当前容量为j
#第i组物体的数量
m = len(goods[i])
for k in range(m): #遍历该组内所有的物体
if goods[i][k][0] <= j: #说明装得下该物体
#如果装
left_v = j - goods[i][k][0] #装了该物体后剩余体积
temp_add = goods[i][k][1] + dp_old[left_v]
#如果不装
temp_not_add = dp_old[j] #则价值等于前i-1组物体的体积
#比较,注意这里也要比较组内前k-1个物体时可能有放得下的情况,更新过dp[j]
dp[j] = max(temp_add,temp_not_add,dp[j])
else: #装不下该物体了
dp[j] = max(dp[j],dp_old[j]) #将组内前面k-1个物体时的最大价值与前i组的最大价值做比较
print(dp)
print(dp[-1])