题解 | #购物单#
购物单
https://www.nowcoder.com/practice/f9c6f980eeec43ef85be20755ddbeaf4
01背包问题中的一种,属于有依赖的背包问题,解决方法是先把有依赖的物品的各种组合列出来作为一组,再当作分组的背包问题去解决。
首先是01背包,本题使用代码解决的基本思路:
使用二维数组的两个下标代表物品序号和背包容量(钱数),数组内的值代表价值(满意度)。
当数组的行号增加1时,表示可供选择的物品增加1。当列号增加1时,表示可供使用的背包容量增加1。
第一行因为可选物品序号为0,所以价值全为0。
同理,第一列因为背包容量为0,所以价值也全为0。
在当前的可选物品前提下,同一行需确保在每次容量增加时都能选择到的物品价值最大。
在当前的背包容量下,同一列需要确保在每次增加可选物品时能选择到的物品价值最大。
满足上面两个条件后,数组的最后一行中的最后一个数即最优解。
t = 5 #假设有5件物品 c = 10 #容量(钱数)为10 i=[[8,6],[10,4],[4,2],[5,4],[5,3]] # 物品信息 [价值, 体积] dp = [[0 for _ in range(c+1)]] print(dp) for n in range(1,t+1): # 行号(物品序号)逐渐递增,t+1代表递增至题目要求的物品序号 tl = [0 for _ in range(c+1)] for v in range(c+1):# 列号(容量)逐渐递增,c+1代表递增至题目要求的容量 if v >= i[n-1][1]: # 背包容量大于当前物品的体积时才有可能选择该物品,物品的下标为n-1 # 假设n=1,n-1即上一行,dp[0]这一行全为0,所以只要背包装得下序号为1的物品(v>=6),其价值都为8 # print(dp[n - 1][v], dp[n - 1][v - i[n-1][1]]) tl[v] = max(dp[n - 1][v], dp[n - 1][v - i[n-1][1]] + i[n-1][0]) # dp[n - 1][v]截至上一物品能选择到的最大价值 # v - i[n-1][1] 意思是减去当前物品所需的容量后剩余的容量,dp[n - 1][v - i[n-1][1]] + i[n-1][0] 意思是剩余容量能装物品的价值加上当前物品的价值 dp.append(tl) print(dp[t][-1])
回到本题,物品最多有两个附件,且如果选择附件必须选择主件,那么主附件可以当成是几种组合,1.主,2.主+附1,3.主+附2,4.主+附1+附2
把这几种组合当成是一组,在选择到有附件的物品时,则再从这一组中选择使得价值最大的组合。
同时,因为取最大值只需要用到第n行和n-1行,所以可以优化为两个数组,而由于取最大值只需要改变v大于物品的体积的一项,可以通过下标递减优化为一个数组。
data = list(map(int, input().split(' '))) l = [[] for _ in range(data[1])] for i in range(data[1]): t = list(map(int, input().split(' '))) l[i] = t + l[i] if t[2] != 0: # 属于附件则把下标存储在主件的信息组里 l[t[2] - 1].append(i) tl = [0 for _ in range(int(data[0] / 10) + 1)] # 钱数是10的倍数 for n in range(data[1]): if l[n][2] == 0: # 是主件则参与比较 m = int(data[0] / 10) # 最大钱数 msum = l[n][0] * l[n][1] # 当前物品满意度 if len(l[n]) == 4: # 有一件附件 anum = l[n][3] # 附件序号 asum = l[anum][0] * l[anum][1] # 附件满意度 while True: tl[m] = max(tl[m], tl[m - int(l[n][0] / 10)] + msum) if m >= (l[n][0] + l[l[n][3]][0]) / 10: # 钱数大于主件+附件价格则进行比较 tl[m] = max(tl[m], tl[m - int((l[n][0] + l[anum][0]) / 10)] + msum + asum) pass m -= 1 if m < l[n][0] / 10: # 钱数少于主件价格则退出比较 break elif len(l[n]) == 5: # 有两件附件 anum1 = l[n][3] anum2 = l[n][4] asum1 = l[anum1][0] * l[anum1][1] asum2 = l[anum2][0] * l[anum2][1] while True: tl[m] = max(tl[m], tl[m - int(l[n][0] / 10)] + msum) if m >= (l[n][0] + l[anum1][0]) / 10: tl[m] = max(tl[m], tl[m - int((l[n][0] + l[anum1][0]) / 10)] + msum + asum1) if m >= (l[n][0] + l[anum2][0]) / 10: tl[m] = max(tl[m], tl[m - int((l[n][0] + l[anum2][0]) / 10)] + msum + asum2) if m >= (l[n][0] + l[anum1][0] + l[anum2][0]) / 10: # 钱数大于主件+两个附件价格则进行比较 tl[m] = max(tl[m], tl[m - int((l[n][0] + l[anum1][0] + l[anum2][0]) / 10)] + msum + asum1 + asum2) m -= 1 if m < l[n][0] / 10: break else: while True: tl[m] = max(tl[m], tl[m - int(l[n][0] / 10)] + msum) m -= 1 if m < l[n][0] / 10: break print(tl[-1])