01背包

注意:
1.本博客仅供参考交流使用,请读者务必自行实践,切勿生搬硬套
2.由于笔者水平有限,若文中有错误或者可以改进之处,欢迎在评论区指出

题目
Description

背包问题(Knapsack problem)是一种组合优化的NP完全问题。01背包问题可以描述为:给定一组物品,每个物品都有自己的重量和价格,在限定的重量内,我们如何选择,才能使得物品的总价格最高。

由于价格、重量一致的不同商品和某些商品能由其他多件商品等价等情况会导致物品选取不唯一,因此本题的测试案例精心选取,不会出现结果不唯一的情况。

Input
第一行输入一个正整数n,表示共有n个商品,每个商品依次编号为1,2,…,n;第二行输入一个正整数c,表示背包的重量为c;第三行按从小到大的商品编号顺序依次输入每个商品的重量,重量为正整数,元素之间以空格隔开;第四行按从小到大的商品编号顺序依次输入每个商品的价格,价格也为正整数,元素之间以空格隔开。

Output
第一行输出背包的最高总价格,第二行按从小到大的顺序输出选中的商品编号,元素间以空格隔开。

Sample Input 1
4
8
2 3 4 5
3 4 5 6

Sample Output 1
10
2 4

分析
这着实是个有趣的问题
我参考了下面两篇博客的思路
Aiden邱秋秋 的博客 01背包
zufe-tmhhh 01背包(需要输出每个物体状态)

第一篇博客启示我:
如果这道题是在采药,就可以理解为,有m个草药可以采,且每个只能采一次(因为如果能采多次的话,那是另一种,疯狂采药,就不是01背包了)就成了我采不采的问题。那到底采不采呢,是这样的,如果我花费了这么多时间去采这个药所收获的价值比我自己原来不采时还多,也就是我用这么多时间来换药的价值很值得,那我就交换。于是我开一个 f 数组来存我现在的价值,就有了下面的转移方程
f[j]=max(f[j],f[j-a[i]]+v[i])
a[i]表示采i这个药所需的时间,v[i]表示采i这个点所获得的价值


我是谁,f是当前点最大价值

我从哪来,f从自己来,也就是max(f[j],f[j-a[i]]+v[i])中的f[j];

我要到哪里去,f要到f[j-a[i]]+v[i]去了。


这其实是个哲学问题

然后第二篇博客启发我只要反过来推测就能知道我是选了哪些物品

def zero_one_bag(c: int, w: list, v: list):
    f = [0] * (c + 1)
    k = [[0] * (c + 1)]
    s = []

    for i in range(1, len(w)):
        for j in range(c, w[i] - 1, -1):
            f[j] = max(f[j], f[j - w[i]] + v[i])
        k.append(f.copy())
    # for j in range(len(k)):
    #     print(k[j])
    e = c

    for i in range(len(k) - 1, 0, -1):
        # print(i)
        # if i == 1:
        #     print("")
        if k[i][e] > k[i - 1][e]:
            # print(k[i][e])
            # print(k[i - 1][e])
            s.append(i)
            e -= w[i]
            if e == 0:
                break
    # print(s)
    s.reverse()
    return [f[c], s]


n = int(input())
c = int(input())
w = list(map(int, input().split(" ")))
v = list(map(int, input().split(" ")))

if min(w) > c:
    print(0)
    print("")
else:
    w = [0] + w
    v = [0] + v

    result = zero_one_bag(c, w, v)

    print(result[0])
    for i in range(len(result[1])):
        print(result[1][i], end=" ")

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值