关于 完全背包
问题描述:
有 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] ,和状态转移方程是完全一致的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix