这道题是01背包的衍生题目
我们由浅入深分析一下
如果没有附件,那么就是一道典型的01背包题目,只是dp[i][j]的定义和递推公式含义有点不一样。
先回顾一下01背包的dp定义:
- dp[i][j]表示选择前i个物品,背包容量为j的情况下能装的最大价值
而此题dp定义为:
- dp[i][j]表示选择前i个物品,金额为j的情况下能买到的最大满意度
01背包递推公式为:
- dp[i][j]=max(dp[i-1][j],dp[i-1][j-vi]+wi) 其中vi为物品i的体积,wi为物品i的价值
而此题的递推公式为:
- dp[i][j]=max(dp[i-1][j],dp[i-1][j-vi]+wi) 其中vi为物品i的体积,wi为物品i的满意度
- wi=vi * pi 其中pi为物品i的重要度
这样就得到了此题没有附件的情况的解法
现在分析有附件的情况
由于附件不能单独出现,所以针对主件i以及他的附件有可能的情况有这几种:
- 不装主件i
- 装主件i
- 装主件i+附件1
- 装主件i+附件2
- 装主件i+附件1+附件2
那么我们只需要在上面没有附件的基础上加上一个枚举——在分析到主件i的时候枚举上面这5种情况即可,然后dp[i][j]取其中的最大值。
n,m=map(int,input().split())
a=[]
for i in range(m):
a.append([int(j) for j in input().split()])
if m==1:#特判只有一个物品,事实证明这道题测试数据没有需要特判的
if a[0][0]<=n:#买得起
print(a[0][0]*a[0][1])
exit()
zhu=[]#主件
fu={}#附件,键存储主件编号,值存储每一个附件
for i in range(m):#分别存储主件和附件
if a[i][2]==0:#是主件
zhu.append(a[i]+[i+1])#将主件编号附在后面
else:#是附件
if a[i][2] in fu:#附件编号已经存在
fu[a[i][2]].append(a[i])#添加第二个新的附件
else:
fu[a[i][2]]=[a[i]]#添加第一个新的附件
size1=len(zhu)#物品数量
dp=[[0 for i in range(n+1)] for j in range(size1)]#注意钱数要开n+1,这样下标才能到n
for i in range(0,n+1):#初始化第一行,遍历钱数
idx=zhu[0][3]#第一行对应的物品的主件编号
if idx in fu:#如果第一行这个主件有附件
if len(fu[idx])==1:#长度为1,只有一个附件,枚举单主件、主件+附件
if i>=zhu[0][0]:
dp[0][i]=zhu[0][0]*zhu[0][1]
if i>=zhu[0][0]+fu[idx][0][0]:
dp[0][i]=zhu[0][0]*zhu[0][1]+fu[idx][0][0]*fu[idx][0][1]
else:#长度为2,枚举单主件,max(主件+附件1,主件+附件2),主件+附件1+附件2
if i>=zhu[0][0]:
dp[0][i]=zhu[0][0]*zhu[0][1]
if i>=zhu[0][0]+fu[idx][0][0]:#主件+附件1
dp[0][i]=max(dp[0][i],zhu[0][0]*zhu[0][1]+fu[idx][0][0]*fu[idx][0][1])
if i>=zhu[0][0]+fu[idx][1][0]:#主件+附件2
dp[0][i]=max(dp[0][i],zhu[0][0]*zhu[0][1]+fu[idx][1][0]*fu[idx][1][1])
if i>=zhu[0][0]+fu[idx][0][0]+fu[idx][1][0]:#主件+附件1+附件2
dp[0][i]=zhu[0][0]*zhu[0][1]+fu[idx][1][0]*fu[idx][1][1]+fu[idx][0][0]*fu[idx][0][1]
else:
if i>=zhu[0][0]:
dp[0][i]=zhu[0][0]*zhu[0][1]
for i in range(1,len(zhu)):#遍历主件
for j in range(0,n+1):#遍历钱数
if j<zhu[i][0]:#买不起
dp[i][j]=dp[i-1][j]
else:
idx=zhu[i][3]#此主件的编号
if idx in fu:#有附件
if len(fu[idx])==1:#长度为1,枚举单主件、主件+附件
vi=zhu[i][0]
wi=zhu[i][0]*zhu[i][1]
dp[i][j]=max(dp[i-1][j],dp[i-1][j-vi]+wi)#比较不拿此主件和拿此主件哪个大
if j>=zhu[i][0]+fu[idx][0][0]:
vi=zhu[i][0]+fu[idx][0][0]
wi=zhu[i][0]*zhu[i][1]+fu[idx][0][0]*fu[idx][0][1]
dp[i][j]=max(dp[i][j],dp[i-1][j-vi]+wi)#如果拿主件+附件更大那就更新
else:#长度为2,枚举单主件,max(主件+附件1,主件+附件2),主件+附件1+附件2
vi=zhu[i][0]
wi=zhu[i][0]*zhu[i][1]
dp[i][j]=max(dp[i-1][j],dp[i-1][j-vi]+wi)
if j>=zhu[i][0]+fu[idx][0][0]:#主件+附件1
vi=zhu[i][0]+fu[idx][0][0]
wi=zhu[i][0]*zhu[i][1]+fu[idx][0][0]*fu[idx][0][1]
dp[i][j]=max(dp[i][j],dp[i-1][j-vi]+wi)
if j>=zhu[i][0]+fu[idx][1][0]:#主件+附件2
vi=zhu[i][0]+fu[idx][1][0]
wi=zhu[i][0]*zhu[i][1]+fu[idx][1][0]*fu[idx][1][1]
dp[i][j]=max(dp[i][j],dp[i-1][j-vi]+wi)
if j>=zhu[i][0]+fu[idx][0][0]+fu[idx][1][0]:#主件+附件1+附件2
vi=zhu[i][0]+fu[idx][0][0]+fu[idx][1][0]
wi=zhu[i][0]*zhu[i][1]+fu[idx][0][0]*fu[idx][0][1]+fu[idx][1][0]*fu[idx][1][1]
dp[i][j]=max(dp[i][j],dp[i-1][j-vi]+wi)
else:#没有附件,只讨论主件取或不取
dp[i][j]=max(dp[i-1][j],dp[i-1][j-zhu[i][0]]+zhu[i][0]*zhu[i][1])
print(dp[size1-1][n])

京公网安备 11010502036488号