题解 | #购物单#

购物单

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])

全部评论

相关推荐

尊嘟假嘟点击就送:加v细说,问题很大
点赞 评论 收藏
分享
评论
14
4
分享
牛客网
牛客企业服务