背包问题 (附单调队列优化多重背包

01背包

 多种物品,每种物品只有一个.求能获得的最大总价值.

 我们考虑是否选择第i件物品时,是需要考虑前i-1件物品对答案的贡献的.

分析

 如果我们不选择第i件物品,那我们就相当于是用i-1件物品,填充了体积为v的背包所得到的最优解.

 而我们选择第i件物品的时候,我们要得到体积为v的背包,我们需要通过填充用i-1件物品填充得到的体积为v-c[i]的背包得到体积为v的背包.

//请保证理解了上面加粗的字再往下看.

所以根据上面的分析,我们很容易设出01背包的二维状态

f[i][v]f[i][v]代表用i件物品填充为体积为v的背包得到的最大价值.

从而很容易的写出状态转移方程

f[i][v]=max(f[i-1][v],f[i-1][v-c[i]]+w[i])f[i][v]=max(f[i−1][v],f[i−1][v−c[i]]+w[i])

状态转移方程是如何得来的?

对于当前第ii件物品,我们需要考虑其是否能让我们得到更优解.

显然,根据上面的话

我们选择第i件物品的时候,我们要得到体积为v的背包,我们需要通过填充用i-1件物品填充得到的体积为v-c[i]的背包得到体积为v的背包.

我们需要考虑到v-c[i]v−c[i]的情况.

当不选当前第ii件物品的时候,就对应了状态转移方程中的f[i-1][v]f[i−1][v],

而选择的时候就对应了f[i-1][v-c[i]]+w[i]f[i−1][v−c[i]]+w[i].

Q:是不是在状态转移方程中一定会选择当前i物品?

A:不会

我们考虑一个问题.

如果一个体积为5的物品价值为10,而还有一个体积为3的物品价值为12,一个体积为2的物品价值为8.显然我们会选择后者.

这样我们的状态转移方程中就不一定会选择i物品。

其实最好地去理解背包问题的话,还是手跑一下这个过程,会加深理解。

代码写法↓

for(int i=1;i<=n;i++)//枚举 物品 
    for(int j=1;j<=V;j++)//枚举体积 
    //这个位置是可以正序枚举的.  qwq
    //一维01背包必须倒叙  emmm
    //这个没错a emmm  
    //采药可以过的  qwq
        if(j>=c[i])
            f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i]);//状态转移方程.
        else f[i][j]=f[i-1][j].

        //上面的if语句是判断当前容量的背包能否被较小体积的背包填充得到.
        //显然 如果j-c[i]<0我们无法填充
        //(谁家背包负的体积啊 (#`O′)

但是二维下的01背包我们还是无法满足,怎么办?

考虑一维如何写!

仔细观察会发现,二维状态中,我们的状态每次都会传递给i(就是说我们的前几行会变得没用.)

这就给了我们写一维dp的机会啊

所以我们理所当然地设状态f[i]f[i]代表体积为i的时候所能得到的最大价值.

则,一维的状态转移方程是f[j]=max(f[j],f[j-c[i]]+w[i]).f[j]=max(f[j],f[j−c[i]]+w[i]).

容易发现的是,我们的f[i]f[i]只会被i以前的状态影响.

如果我们顺序枚举,我们的f[i]f[i]可能被前面的状态影响.

所以我们考虑倒叙枚举,这样我们的f[i]f[i]不会被i以前的状态影响,而我们更新的话也不会影响其他位置的状态.

(可以手绘一下这个过程,应该不是很难理解.)

或者来这里看看(可能图画的有点丑了

代码写法↓

for(int i=1;i<=n;i++)//枚举 物品 
    for(int j=V;j>=c[i];j--)//枚举体积 
        f[j]=max(f[j],f[j-c[i]]+w[i]);//状态转移方程. 

//应该不是很难理解.

小结

01背包问题是背包问题中最基础,也是最典型的问题.其状态转移方程也是基础,更可以演变成其他背包的问题.

请保证看懂之后再向下看.

完全背包

for(int i=1;i<=n;i++)//枚举物品
    for(int j=c[i];j<=V;j++)//枚举体积.注意这里是顺序/
        f[j]=max(f[j],f[j-c[i]]+w[i]);//状态转移.

疯狂的采药 - 洛谷

t,m1=list(map(int,input().split()))
yao=[None,]
for i in range(m1):
    q,q1=list(map(int,input().split()))
    yao.append({'w':q,'v':q1})
m=[[0]*(t+1)for _ in range(m1+1)]
dp=[0]*(t+1)
if m1==1:
    print(t//yao[1]['w']*yao[1]['v'])
else:
    #二维
    # for i in range(1,m1+1):
    #     for j in range(yao[i]['w'],t+1):
    #         if yao[i]['w']>j:
    #             m[i][j]=m[i-1][j]
    #         else:
    #             m[i][j]=max(m[i-1][j],m[i][j-yao[i]['w']]+yao[i]['v'])
    # print(m[m1][t])

    #一维
    for i in range(1, m1 + 1):
        # for j in range(t,yao[i]['w']-1,-1):
        for j in range(yao[i]['w'] , t+1):
            dp[j]=max(dp[j],dp[j-yao[i]['w']]+yao[i]['v'])
    print(dp[t])
print(dp)

多重背包

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值