背包问题

一、01背包

dp[i][j]->第i个物品,放在背包里,总重量不超过j的前提下,所获得的最大价值。
状态转移方程
如果第i个物品大于所剩的容量ci>j,则不能放这个物品,最大值为之前的最大值。
dp[i][j]=dp[i-1][j]
如果第i个物品小于所剩容量 ci<j,这时可以选择放或者不放,放了则要选择之前为j-ci容量的背包,才能保证放了之后的总容量为j。dp[I][j]=max(dp[i-1][j],dp[i-1][j-ci]+wi).

for (int i = 1; i <= N; ++i) {
    for (int j = 0; j <= V; ++j) {
        if(j >= c[i]) {
            dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]);
        } else {
            dp[i][j] = dp[i - 1][j];
        }
    }
}

01背包的空间优化

我们可以把dp数组只开成一维表示体积,然后从大到小的枚举体积,也就是从V到0枚举。
注意!!!01背包是从大到小枚举体积。
转移方程为:dp[j]=max(dp[j-ci]+wi,dp[j]).

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

二、多重背包

每种物品有ni个
解法:在状态转移过程中枚举k,代表第i个物品的选取次数,将多重背包转换成01背包
dp[i][v]=max(dp[i][v],dp[i-1][v-kci]+kwi);

for (int i = 1; i <= N; i++) {
    for (int j = 0; j <= V; j++) {
        for (int k = 0; k <= n[i]; k++) {
            if (j >= c[i] * k) {
                dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]);
            }
        }
    }
}

注意,当k=0时候,dp[i][j]=max(dp[i-1][j],dp[i][j]);相当于01背包中的else部分,选择放或者不放

多重背包的空间优化

多重背包可以转化为01背包,依然可以像01背包一样优化空间,依然按照从大到小的顺序枚举背包的体积。

for (int i = 1; i <= N; i++) {
    for (int j = V; j >= 0; j--) {
        for (int k = 1; k <= n[i]; k++) {
            if (j >= c[i] * k) {
                dp[j] = max(dp[j - c[i] * k] + w[i] * k, dp[j]);
            }
        }
    }
}

三、完全背包

每种物品的数量是无限的
解法:虽然每个物品的数量是无限的,但是背包的容量有限,可以根据背包的容量限制选取的数量,进而转换为多重背包问题,进而转化为01背包问题。

for (int i = 1; i <= N; i++) {
    for (int j = 0; j <= V; j++) {
        for (int k = 0; k * c[i] <= j; k++) {
            dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]);
        }
    }
}

时间优化 找规律发现
直接将01背包中的dp[i-1][j]换成dp[i][j];

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

完全背包的空间优化

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

与01背包相比,完全背包只是第二重循环的顺序发生了反转。

总结

记空间优化后的代码

//01 背包 就是从V开始枚举体积
//dp[j]=max(dp[j-c[i]]+w[i],dp[j]);
for (int i = 1; i <= n; ++i)
    for (int j = v; j >= c[i]; --j) {
        dp[j] = max(dp[j - c[i]] + w[i], dp[j]);
    }

//多重背包
//和01相同 从V开始枚举体积,在枚举体积之后,从k开始枚举选择当前物品的数量
dp[j]=max(dp[j-k*c[i]]+w[i]*k,dp[j])
for (int i = 1; i <= N; i++) {
    for (int j = V; j >= 0; j--) {
        for (int k = 1; k <= n[i]; k++) {
            if (j >= c[i] * k) {
                dp[j] = max(dp[j - c[i] * k] + w[i] * k, dp[j]);
            }
        }
    }
}

//完全背包
完全背包更简单,直接和01背包一样,只不过是从c[i]~V开始枚举体积
dp[j]=max(dp[j-c[i]]+w[i],dp[j]);
for (int i = 1; i <= n; i++) {
    for (int j = c[i]; j <= v; j++) {
        dp[j] = max(dp[j - c[i]] + w[i], dp[j]);
    }
}

使用背包解题时dp[0]初始化的三种情况

如果方案数,那 dp[0] = 1,代表着刚开始没装就 1 种方案
如果求最优解,那 dp[0] = 0,表示什么都没装就是 0
如果求是否可以,那 dp[0] = true,表示什么都没装就可以装到 0 位置

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 动态规划认知 ​ 在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。 ...
    汝其母戏吾乎阅读 1,639评论 0 1
  • 技术交流QQ群:1027579432,欢迎你的加入! 欢迎关注我的微信公众号:CurryCoder的程序人生 1....
    CurryCoder阅读 288评论 0 1
  • 0 1 背包问题描述 有n个物品,它们有各自的体积和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值...
    justonemoretry阅读 660评论 1 0
  • 背包问题 有N种物品和一个容量为W的背包。第i种物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这...
    jdzhangxin阅读 893评论 0 4
  • 动态规划三个重要性质: 最优子结构 重叠子问题 无后效性(在构造解空间时一定要考虑) 一. 0/1背包 问题描述 ...
    ChongmingLiu阅读 2,801评论 0 1