背包dp学习笔记

背包 dpdp

顾名思义,就是给你一个背包,然后给你一些物品,有一定的限制条件,然后求获得的最大值。

Part1 01背包

P1048 采药

大意:给一个背包,每个物品有对应的价值,重量(本题中时间就是重量),在背包容量下(采药的时间),求最大价值。

0101 背包二维方程

dp[i][j]=max(dp[i1][j],dp[i1][jw[i]]+c[i])dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i])

稍微解释一下,dp[i][j]dp[i][j]ii 个物品,取 jj 的容量可以获得的最大价值。

dp[i1][j]dp[i-1][j] 表示我不选当前这一件物品。

dp[i1][jw[i]]+c[i]dp[i-1][j-w[i]]+c[i] 表示我选当前这一件物品,首先我需要腾出 w[i]w[i] 的空间,也就是 jw[i]j-w[i] 同时当前物品占一个位置,也就是 i1i-1 最后,可以获得 c[i]c[i] 的价值。

然后考虑压维。

直接设 dp[j]dp[j] 表示重量为 jj 是可以获得的最大值。

dp[j]=max(dp[j],dp[jw]+c)dp[j]=max(dp[j],dp[j-w]+c)

(边输入边进行处理)

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

从后往前枚举是防止重复。原因是如果是从前往后的话,可能会出现在同一个 iidpjdp_j 被更新一次,dpj+wdp_{j+w} 再被更新一次,那这个物品就被取了两次,就不符合 0101 背包了。

方程的解释和上面基本雷同,就不多说了。

CodeCode

练习:AT4526

_JF_的题解

Part2 完全背包

P1616 疯狂的采药

完全背包和 0101 背包不同的地方,就是物品有无数件。

所以状态就不再是取或不取,而是取 00 件,11 件,22 件... nn件。

先考虑朴素算法 dp[i][j]dp[i][j] 表示前 ii 件物品,消耗 jj 的空间,所可以获得最大值。

dp[i][j]=max(dp[i][j],dp[i1][jw[i]×k]+k×c[i])dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]\times k]+k\times c[i])

我们只要再多一层循环,枚举件数即可。

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

接着考虑压维。

dp[j]dp[j] 表示背包容量为 jj 时,所获得的最大价值。

方程就是

dp[j]=max(dp[j],dp[jw]+c)dp[j]=max(dp[j],dp[j-w]+c)

但是要注意,这里的 jj 要从正序开始枚举。

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

Part3 多重背包

和完全背包不同的是,这里有限制物品的数量。

P1776 宝物筛选

先考虑简单的方法。

思路:我将每件物品分成 取 0,1,2...n0,1,2...n 件,然后对取这一些件数进行 0101 背包。

dp[i][j]=max(dp[i1][j],dp[i1][jk×w[i]]+k×c[i]dp[i][j]=max(dp[i-1][j],dp[i-1][j-k\times w[i]]+k\times c[i]

然后考虑压维,就是

dp[k]=max(dp[k],dp[kw[i]]+c[i])dp[k]=max(dp[k],dp[k-w[i]]+c[i])
for(int i=1;i<=n;i++)
	for(int k=m;k>=v[i];k--)
			dp[k]=max(dp[k],dp[k-w[i]]+c[i]),ans=max(ans,dp[k]);

时间复杂度为O(V×n[i])O(V\times \sum n[i])

但是,我们可以进行优化:

首先引入一个定理:对于任意正整数 xx,都可以用这个式子表示:

20+21+22+...+2k+s2^0+2^1+2^2+...+2^k+s

ss 表示 x20...2kx-2^0...-2^k 的剩余部分。

那就说明,我们只用枚举这一些数(202^0,212^1 等),并进行组合,加和,就可以轻松将 00n[i]n[i] 之内所有的数表达出来。

这就对我们的这一行进行了优化。

for(int j=1;j<=n[i];j++)

对于 20+21+22+...+2k+s2^0+2^1+2^2+...+2^k+s,我们只需要 log(n[i])log(n[i]) 的时间预处理即可。

O(V×log n[i])O(V\times \sum log\ n[i])

CodeCode

P1833 樱花

稍微变形。

对于“可以看无数次”,看上去是完全背包,但是由于时间有限制,他最多可以看 m/w[i]m/w[i] 次,于是就转换了。

CodeCode

Part 4 二维费用背包

P1910 L国的战斗之间谍

相较于 0101 背包,就是多了一些限制条件。

0101 背包的限制条件是重量。

但是二维费用背包就是有两个限制条件。

也可能会有更多(那就多加几维)即可。

对于这一题而言,我们设 dp[i][j][k]dp[i][j][k] 表示前 ii 个人花费 jj ,kk 代价以后所获得的最大值。

dp[i][j][k]=max(dp[i1][j][k],dp[i1][jt[i]][kw[i]]+c[i])dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-t[i]][k-w[i]]+c[i])

通过观察,我们可以发现这一类题的套路:

每加一个限制条件,就多加一维,多加一层循环,以及 [lk[i]][l-k[i]] 这样子的 0101 背包模式。

当然,我们可以压去 ii 这一维。

CodeCode

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