关于 完全背包

问题描述:

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


问题特点:

每种物品有无限个,可以选择 i 物品的数量放入背包。


如何得到状态转移方程:

对于01背包来说,我们有选或者不选第 i 个物品这两种方式,那么对于完全背包来说,所以我们可以按照第 i 种物品选多少个来分组,所以我们一共有 k 种方式(虽然分成若干组来做,但 k 不能无限大,因为 i 物品有体积,k 个 i 物品的体积不能超过背包容量)。

考虑前 i 个物品选择了 k 个,这样求此种选法的最大集合并不好求,那么我们可以“曲线救国”:

1、去掉 k 个物品 i

2、求 Max ,dp[ i - 1 ][ j - k * w[i] ]

3、再加回来 k 个物品 i

由此可以得到状态转移方程↓↓↓↓↓↓


状态转移方程:

dp[i][j] = max ( dp[i - 1][j], dp[i - 1][j - k * w[i]] + k * v[i] )


二维三重循环:

非常耗时

for(int i = 1; i <= n; i ++)
        for(int j = 0; j <= m; j ++)
            for(int k = 0; k * v[i] <= j; k ++)
                dp[i][j] = max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);

优化版(一维二重):

for(int i = 1; i <= n; i ++)
        for(int j = v[i]; j <= m; j ++)
                dp[j] = max(dp[j], dp[j - v[i]] + w[i]);

那么这是怎么优化出来的呢?

这样我们就可以从枚举 k 个循环变成枚举两个循环:

for(int i = 1; i <= n; i ++)
    for(int j = 0; j <= m; j ++)
    {
        f[i][j] = f[i-1][j];
        if(j >= v[i]) f[i][j] = max(f[i][j], f[i][j-v[i]]+w[i]);
    }

进一步优化:

for(int i = 1; i <= n; i ++)
        for(int j = v[i]; j <= m; j ++)
            dp[j] = max(dp[j], dp[j - v[i]] + w[i]);

为什么可以优化为一维数组?

 j 是从小到大枚举的,而且 j - v[i] 一定小于 j ,所以我们在算 dp[j] 的时候 dp[j - v[i]] 已经被算过了,就是第 i 层的 j - v[i] ,和状态转移方程是完全一致的。


 

模板题:https://www.acwing.com/problem/content/3/

posted @   爱吃虾滑  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示