这道题是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])