0-1背包DP自学笔记

0-1背包

  • 这种背包每个物品只有拿与不拿两种状态

转移方程推导:

设 DP 状态 fi,j为在只能放前 i 个物品的情况下,容量为 j 的背包所能达到的最大总价值。
考虑转移。假设当前已经处理好了前 i1 个物品的所有状态,那么对于第 i 个物品,当其不放入背包时,背包的剩余容量不变,背包中物品的总价值也不变,故这种情况的最大价值为 fi1,j ;当其放入背包时,背包的剩余容量会减小 wi ,背包中物品的总价值会增大 vi ,故这种情况的最大价值为 fi1,jwi+vi
由此可以得出状态转移方程:

fi,j=max(fi1,j,fi1,jwi+vi)

这里如果直接采用二维数组对状态进行记录,会出现 MLE。可以考虑改用滚动数组的形式来优化。
由于对 f[i] 有影响的只有 f[i-1] ,可以去掉第一维,直接用 f[i] 来表示处理到当前物品时背包容量为 i 的最大价值,得出以下方程:

fj=max(fj,fjwi+vi)

例题

n个物品和一个容量为 W的背包,每个物品有重量 wi 和价值 vi两种属性,要求选若干物品放入背包使背包中物品的总价值最大且背包中物品的总重量不超过背包的容量。

思路

  • 这道就是一个非常简单的背包模板,可以直接套用上文公式进行求解

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=20000;
int n,W,w[maxn],v[maxn],f[maxn];
int main() {
	cin>>n>>W;
	for (int i = 1; i <= n; i++) cin>>w[i]>>v[i];
	for(int i=1;i<=n;i++){
		for(int j=W;j>=w[i];j--){
			f[j]=max(f[j-w[i]]+v[i],f[j]);
		}
	}  
  	cout<<f[W];
  return 0;
}

注意!!!

初学很容易写出错误核心代码:

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

这段代码枚举顺序错了。
仔细观察代码可以发现:对于当前处理的物品 i 和当前状态 fi,j ,在 jwi 时, fi,j 是会被 fi,jwi 所影响的。这就相当于物品 i 可以多次被放入背包,与题意不符。(事实上,这正是完全背包问题的解法)

为了避免这种情况发生,我们可以改变枚举的顺序,从 W 枚举到 wi ,这样就不会出现上述的错误,因为 fi,j 总是在 fi,jwi 前被更新。

因此实际核心代码为:

for(int i=1;i<=n;i++){
	for(int j=W;j>=w[i];j--){
		f[j]=max(f[j],f[j-w[i]]+v[i]);
	}
}
posted @   WJX120423  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示