HJ16 题解 | #购物单#

购物单

https://www.nowcoder.com/practice/f9c6f980eeec43ef85be20755ddbeaf4

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

#华为##华为od##华为机试##华为od机试##动态规划#
华为HJ103所有解法 文章被收录于专栏

这是我准备华为od面试的专属专栏,我会把自己的解法更新在里面,我会尽量写清楚自己的思路以及多写关键注释,希望对阅读的人有帮助~~~

全部评论

相关推荐

01-14 10:23
已编辑
湖南师范大学 计调
太久没更新,前几天看到一条评论,说“牛客就是当年那群做题区毕业了开始找工作还收不住那股味”的群体。字里行间透着居高临下的评判,不是,他该不会以为自己很幽默?很犀利吧?作为在牛客混了不算短日子的用户,我感到的不只是被冒犯,更是一种深刻的悲哀——这种以“松弛感”为名,对另一种生存策略的轻蔑,颇有一种自己考不上大学早早出来混社会,嘲笑考上大学的人是书呆子,然后大言不惭地说:死读书有什么用,人脉和资源才是硬道理。我不知道说这个话的人,手头究竟握着多少真正管用的人脉与资源,也不知道他这么傲慢地说出“那股味”的时候,是站在哪一个巨人的肩膀上,才能如此“松弛从容”地俯视众生,还能品评出别人身上“没收住”的余...
淬月星辉:这种评论把正常的努力扭曲成卷😂,说白了就是自己不努力,看着身边努力的人一个个都事业有成了,自己的心里开始不平衡了,就发这种酸言酸语。牛客可以说是我用过那么多平台里社区氛围最好的论坛了,用了大半年了,基本上没见过有人吵架的,都是在互帮互助提建议,帮忙看简历的,帮忙选offer的,帮忙指点学习路线的,分享工作经验和趣事的,我觉得这才是互联网该有的样子。
点赞 评论 收藏
分享
2025-12-10 14:51
门头沟学院 Java
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务