常见背包问题大总结,包含大量案例练习,通俗易懂

10 篇文章 0 订阅

01.0-1背包

了解01背包

n 件物品和一个最大承重为 W 的背包,每件物品的重量是 wi价值是 vi,在保证总重量不超过 W 的前提下,选择某些物品装入背包,背包的最大总价值是多少?每个物品只有 1 件,也就是每个物品只能选择 0 件或者 1 件。( values 是价值数组,weights 是重量数组)

状态转移方程

定义状态:dp(i, j) 是最大承重为 j、有前 i 件物品可选时的最大总价值

dp(i, j) = max { dp(i – 1, j), dp(i – 1, j – weights[i – 1]) + values[i – 1] }
伪代码

for (int i = 1; i <= n; i++) //n件物品	i表示前i个物品
	for (int j = 1; j <= w; j++) //最大承重为 W
		if(w[i]>j) dp[i][j]=dp[i-1][j];
		else  dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);

状态转移方程压缩

dp(j) = max { dp(j), dp(j – weights[i – 1]) + values[i – 1] } 注意(要逆向推导,具体如下)

for (int i = 1; i <=n; i++) {//n件物品	i表示前i个物品
	for (int j = w; j >=w[i]; j--) {//当j < w[i],证明第i件物品不能选(必须要逆向推)
		dp[j]=Math.max(dp[j], dp[j-w[i]]+v[i]);
	}
}	

练习与题解

  • P1049 [NOIP2001 普及组 装箱问题:https://www.luogu.com.cn/problem/P1049
    Java题解:https://blog.csdn.net/qq_46237746/article/details/121682567
  • P2370 yyy2015c01 的 U 盘:https://www.luogu.com.cn/problem/P2370
    Java题解:https://blog.csdn.net/qq_46237746/article/details/123687777

02.完全背包

了解完全背包

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。第 i 种物品的重量是 w[i]价值是 v[i]。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。

基于0-1背包的完全背包

for (int i = 1; i <=N; i++) {//有 N 种物品 i表示前i种物品
	for (int j = V; j >=w[i]; j--) {//从右到左进行覆盖,当j < weights[i],证明第i件物品不能选
		for (int k = 0; k <= j/w[i]; k++) {
			dp[j]=Math.max(dp[j], dp[j-k*w[i]]+k*v[i]);
		}
	}
}

完全背包状态转态移方程

定义状态:dp(i, j) 是最大承重为 j、有前 i 件物品可选时的最大总价值
则其状态转移方程为:dp[i][j]=Math.max( dp[i-1][j], dp[i][j-w[i]]+v[i] )

状态压缩

dp(j) = max { dp(j), dp(j – w[i]) + v[i] } 注意(要从小到大递推)
具体如下:

for (int i = 1; i <= N; i++) 
	for (int j = w[i]; j <= V; j++) 
		dp[j]=Math.max(dp[j], dp[j-w[i]]+v[i]);

练习与题解

  • P1853 投资的最大效益:https://www.luogu.com.cn/problem/P1853
    Java题解:https://blog.csdn.net/qq_46237746/article/details/123909002
  • P5662 [CSP-J2019] 纪念品:https://www.luogu.com.cn/problem/P5662
    Java题解:https://blog.csdn.net/qq_46237746/article/details/123561594

03.多重背包

了解多重背包

有 N 种物品和一个容量是 V 的背包,每种物品都有n[i]可用。第 i 种物品的重量是 wi价值是 vi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。

状态转移方程

	for (int i = 1; i <= n; i++)   //有N种物品
		for (int j = v; j>=0; j--) 	//容量v
			for (int k = 1; k <= n[i]; k++)    //第i个物品的数量为n[i]
				if(j>=k*w[i])	
					dp[j]=Math.max(dp[j], dp[j-k*w[i]]+k*v[i]);

		for (int i = 1; i <= n; i++) //有N种物品
			for (int k = 1; k <= n[i]; k++)  //第i个物品的数量为n[i]
				for (int j = v; j >= w[i]; j--) //容量v
					dp[j]=Math.max(dp[j], dp[j-w[i]]+v[i]);

练习与题解

  • P2347 [NOIP1996 提高组] 砝码称重:https://www.luogu.com.cn/problem/P2347
    Java题解:https://blog.csdn.net/qq_46237746/article/details/123854837
  • P6771 [USACO05MAR]Space Elevator 太空电梯:https://www.luogu.com.cn/problem/P6771
    Java题解:https://blog.csdn.net/qq_46237746/article/details/123855331

04.二维费用背包

了解二维费用背包

有n件物品,两种不同的费用,选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为a[i]和 b[i]。两种代价可付出的最大值(两种背包容量)分别为V和U。物品的价值为w[i]

状态转移方程

定义状态:dp[k][i][j]背包两种容量分别为i,j的情况下,前k件物品可选择时物品的最大价值

dp[k][i][j]=Math.max(dp[k-1][i][j], dp[k-1][i-a[k]][j-b[k]]+w[k]) //a[k]<=i && b[k]<=j
dp[k][i][j]=dp[k-1][i][j];    //不满足条件:a[k]<=i && b[k]<=j

具体案例

		for (int k = 1; k <= n; k++) //有n件物品
			for (int i = 1; i <= V; i++)  //背包最大容量V
				for (int j = 1; j <= U; j++) {	//背包最大容量U
					if(i>=a[k]&&j>=b[k]) {
						dp[k][i][j]=Math.max(dp[k-1][i][j], dp[k-1][i-a[k][j-b[k]+1);
					}else {
						dp[k][i][j]=dp[k-1][i][j];
					}
				}

状态压缩

dp[k][i][j]=Math.max(dp[k-1][i][j], dp[k-1][i-a[k][j-b[k]+w[k])   //逆向推导(从大到小)

具体案例

		for (int k = 1; k <= n; k++) 
			for (int i = V; i >= a[k]; i--) 
				for (int j = U; j >= b[k]; j--) 
					dp[i][j] = dp[i - a[k][j - b[k]] + w[k];

练习与题解

  • 榨取kkksc03):https://www.luogu.com.cn/problem/P1855
  • P1509 找啊找啊找GF:https://www.luogu.com.cn/problem/P1509
    Java题解:https://blog.csdn.net/qq_46237746/article/details/123776023

05.分组背包问题

了解分组背包

分组背包:有groupNum组物品,N个物品,一个容量是capacity的背包。每组物品有若干个,同一组内的物品最多只能选一个。

状态转移方程

定义状态: dp[k][j] 最大承重为j,有前k组物品可选时的最大价值

for (int k = 1; k <= groupNum; k++) // 所属组k
	for (int j = 0; j <= capacity; j++) // 容量j
		for (int i = 0; i < I; i++) // 所属分组k的物品I
			dp[k][j]=Math.max(dp[k - 1][j],
				dp[k - 1][j - 物品i的重量] + 物品i的价值)

状态转移方程压缩

定义状态:dp[j] 最大容量为j的前提下,物品的最大价值

for (int k = 1; k <= groupNum; k++) // 所属组k
	for (int j = capacity; j >= 0; j--) // 容量j
		for (int i = 0; i < I; i++) // 所属分组k的物品I
			dp[j]=Math.max(dp[j], dp[j-物品i的重量] + 物品i的价值);

练习与题解

  • P1757通天之分组背包:https://www.luogu.com.cn/problem/P1757
    Java题解:https://blog.csdn.net/qq_46237746/article/details/121941066
  • P5322 [BJOI2019] 排兵布阵:https://blog.csdn.net/qq_46237746/article/details/123850976
    Java题解:https://blog.csdn.net/qq_46237746/article/details/123850976

06.混合背包

前面几种背包混合
案例:https://www.luogu.com.cn/problem/P1833

07.有依赖的背包/树背包

https://oi-wiki.org/dp/tree/#_3

08.补充

PS:参考资料

  • 洛谷刷题题单:https://www.luogu.com.cn/training/8917
  • (yyds)https://oi-wiki.org/dp/knapsack/
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值