背包问题(一) 01背包

题目释义#

有一个背包容量为 m 的背包,n 个物品。每个物品的重量为 w,价值为 v

要求在选取物品总重量不大于背包容量的情况下,使得选取物品总价值最大。

每种物品仅可使用一次。

分析#

首先,我们用 f[i][j] 表示前 i 个物品放入容量为 j 的背包的最大价值。

对于每个物品,我们只有两个选择,即放和不放。

  • 若放入背包,则 f[i][j]=f[i1][jw[i]]+v[i]

  • 若不放入背包,则 f[i][j]=f[i1][j]

  • 但我们还要考虑,背包容量不足时也不能放入背包。

因此,状态转移方程为

f[i][j]=max(f[i1][j],f[i1][jw[i]]+v[i]);

Code:#

for(int i = 1;i <= n;i++)// 依次枚举每个物品; 
	for(int j = m;j >= 0;j--)// 枚举背包容量; 
		if( j >= w[i] )// 放得下; 
			f[i][j] = max(f[i-1][j],f[i-1][ j-w[i] ]+v[i]);
		else// 放不下; 
			f[i][j] = f[i-1][j];

以上的代码时间复杂度空间复杂度均为 O(nm),若题目中给出 nm 10000 ,它可能就会出现 MLE 的情况。

优化#

我们发现,f[i][j] 只与 f[i1][...] 有关,那我们可以优化掉数组的第一维,直接用 f[j]
来表示处理到当前物品时背包容量为 j 时的最大价值,
得出以下方程:

f[j]=max(f[j1],f[jw[i]]+v[i]);

注意: 为防止之前的值被覆盖,需要逆向枚举背包容量。

而原先未经优化的代码无需考虑背包容量的枚举顺序。

空间复杂度由 O(nm) 降至 O(m)

Code#

for(int i=1;i<=n;i++)
	for(int j=m;j>=w[i];j--)// 逆向枚举;
		f[j]=max(f[j],f[ j-w[i] ]+v[i]);

例题#

P2871 [USACO07DEC]Charm Bracelet S(模板)

P1048 [NOIP2005 普及组] 采药(模板)

P1049 [NOIP2001 普及组] 装箱问题

P1060 [NOIP2006 普及组] 开心的金明

作者:白简

出处:https://www.cnblogs.com/baijian0212/p/01beibao.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   -白简-  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu