无限背包(疯狂采药)

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

题目

Description

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的重量是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

【备注:读入一行数字可用list(map(int,input().split()))来完成。因为数据较大,如果用for可能会超时】

Input
第一行是N种物品。第二行是背包容量V。接下去2行,第一行是各个物品的重量c[i],第二行是各个物品的价值w[i]。

Output
一个数字,价值总和。

Sample Input 1
3
2
1 2 3
1 2 3

Sample Output 1
2

这个题目最开始,可能是叫疯狂采药,其实是同一个题来着,也就是物品重量相当于采药所花时间,物品价值相当于药草价值,背包容量相当于总的采药时间,为了让这个问题更有哲学美感,我决定从采药的角度来叙述这个问题

疯狂采药(无限背包)和小心采药(01背包)的本质区别是什么?
01背包
本质是有限和无限的区别,同一颗药草可以采有限次和无限次的区别,当然我们知道由于总时间的限制,我们不可能真正的无限地采一个药草,但是当我们不清楚总时间或者总时间相当大的时候,采同一个药草仿佛真的可以认为是无限次,那么我们要怎么解决无限的问题呢?

在这里我提供两种解决无限问题的思路,一种过了OJ的测试(1530ms),另一种则没有(1950ms),要求是1600ms内。这两种方法,我叫第一种为转换视角法,第二种为一生万物法,看起来很中二,但是我觉得采药问题确实很有哲学艺术感。我们先二后一地来看一下。

Coding1:(超时版本,分析在后面)

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

f = [0] * (c + 1)

nw = [0] * 1000000
nv = [0] * 1000000

id = 0

for i in range(len(w)):
    num = c // w[i]
    if num != c * w[i]:
        num += 1
    s = 1
    while num > 0:
        id += 1
        nw[id], nv[id] = s * w[i], s * v[i]
        num -= s
        s *= 2

for i in range(1, id + 1):
    for j in range(c, nw[i] - 1, -1):
        f[j] = max(f[j], f[j - nw[i]] + nv[i])
        if i == id:
            break

print(f[c])

可以发现我们先将w,v两个数组进行了扩充处理,变成nw,nv。
其实就是这么想的,既然我可以采这个药好多次,那么我就可以认为a药草其实本质上是花时为2w[a],价值为2v[a]的药草,然后是3,4…的集合,我多了好多种药草,那为什么程序里都只有偶数倍,没有奇数倍呢,是因为在这种问题里流传一句话,叫“1 2 4 8可以构成一切数(正整数)”,也就是在动态规划里,只需要考虑偶数倍,其实已经考虑了奇数倍在里面,大家可以体会一下。
所以我叫它一生万物,这是一种认为一个事物在时域上扩展后可以变成无数个事物的哲学思想。

Coding2:(通过版本,分析在后面)

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

    for i in range(1, len(w)):
        for j in range(w[i], c + 1):
            f[j] = max(f[j], f[j - w[i]] + v[i])

    return [f[c]]


n = int(input())
c = int(input())
w = list(map(int, input().split(" ")))
v = list(map(int, input().split(" ")))
w = [0] + w
v = [0] + v

print(full_bag(c, w, v)[0])

是的,通过版本是如此的简洁。所以没有简洁美的程序,一般都架不住考验。为什么能如此简洁呢,其实这和01背包问题的核心问题只有一句不一样。
for j in range(c, w[i] - 1, -1):(01)
for j in range(w[i], c + 1):(无限)
是的,如果我们在时间轴上换个方向看,有限就变成了无限。

在01背包中,我们采用一维数组逆序表示的原因是,我们一维空间无法保存i-1轮的状态,所以我们只能逆序,这样f[j-w[i]]就是上一轮的解,保证在i-1轮肯定没有选到过物品i,可以更新。

在完全背包中,我们的物品是无限拿的,所以要顺序执行,这样可能同一轮会选择多次,也就是同一物品选择多次

所以这两种解法,本质上来说呢,可以让我们知道,从一个角度看,时间是稠密丰富的,从另一个角度看,时间是简明流畅的

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
c 01背包问题是一种经典的背包问题,其中给定一个背包的容量和一组物品,每个物品有一个重量和一个价值。目标是选择物品放入背包中,使得放入的物品总重量不超过背包容量,并且总价值最大化。 采药问题是c 01背包问题的一个具体应用场景。在采药问题中,给定一个草药园和一些草药的价值和采摘时间,每种草药在一定的时间内可以采摘得到一定的价值。采药者只有有限的时间来采摘草药,他需要选择哪些草药采摘,并且使得采摘的草药的总价值最大化。 思路和解法可以参考引用和引用中提供的链接和提示。其中,引用提供了洛谷和AcWing中关于c 01背包问题的其他约束条件和实现方式的说明,引用提供了优化思路和解法的具体描述。 总结来说,c 01背包采药问题是一个经典的背包问题的具体应用,目标是在有限时间内选择草药使得总价值最大化。具体的解法可以参考引用和引用中提供的链接和提示。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [[C++]背包问题(1):洛谷 采药 01背包模型详解](https://blog.csdn.net/weixin_62712365/article/details/124640545)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [[AcWing] 423. 采药(C++实现)01背包问题](https://blog.csdn.net/weixin_43972154/article/details/124313102)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值