完全背包问题完全背包求具体方案

这个博客想带大家通透的了解完全背包(我早就学完了)

完全背包是什么

相较于 01 背包,完全背包就是每个物品的数量有无数个,而 01 背包则是每个物品只能选一个。

怎么解决

我们定义 f [ i ] [ j ] f[i][j] f[i][j] 表示前 i i i 个物品,背包容量为 j j j 的最大价值。
若我们第 i i i 个物品一个也不选就是 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j] 这是很简单的。
但是假如我要选物品呢?
而这时有点难办:
为什么呢?
因为你不知道我们要挑选多少个,但是我们如果枚举那是不是太笨了呢?
而完全背包的发明者也巧妙地想到并解决了这个问题:
我们可以交给之前算过的 f [ i ] [ j − w [ i ] ] f[i][j-w[i]] f[i][jw[i]] ( w [ i ] w[i] w[i] 表示当前物品的体积)
为什么呢,因为你不能像 01 背包一样直接写成 f [ i − 1 ] [ j − w [ i ] ] f[i-1][j-w[i]] f[i1][jw[i]]
因为假如写的 i − 1 i-1 i1 就是直接只选了一遍你就走了但是完全背包有无数个那当然不行啊。

怎么优化

首先时间已经是不能优化了。
那么我们只能考虑优化空间。
滚动数组优化依然是可以的,但我们来着重讲讲降维优化,我们先看看代码:

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

我们发现唯一不同的就是枚举顺序,01 背包是倒序枚举而完全背包是正序枚举。
我们来回顾一下 01 背包的优化:
f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − w [ i ] ] + c [ i ] ) f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+c[i]) f[i][j]=max(f[i1][j],f[i1][jw[i]]+c[i])
而 01 背包要倒序枚举,为什么?
因为假如正序枚举 我们此时调用的 f [ j − w [ i ] ] f[j-w[i]] f[jw[i]] 已经被更新过了。
所以需要倒序枚举。而此时我们回头一看,诶这不就是完全背包所需要的吗。完全背包调用的 f [ i ] [ j − w [ i ] ] f[i][j-w[i]] f[i][jw[i]] 就是要被更新过的啊!
蓦然回首,那人却在,灯火阑珊处。

拓展

完全背包求具体方案

题目大意如同其名。

我们做完全背包时可以记录每个物品的个数。

记录 p o s [ i ] [ j ] pos[i][j] pos[i][j] 表示第 i i i 个物品且当前背包容量为 j j j 的选的个数。

所以我们就假如转移到 f [ i ] [ j − w [ i ] ] + c [ i ] f[i][j-w[i]]+c[i] f[i][jw[i]]+c[i] 的时候把 p o s pos pos 更新一下就可以了。
p o s [ i ] [ j ] = p o s [ i ] [ j − w [ i ] ] + 1 pos[i][j]=pos[i][j-w[i]]+1 pos[i][j]=pos[i][jw[i]]+1

代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 105, M = 1005;
int n, m, w[N], c[N], f[N][M], pos[N][M];
void print(int x, int y) {
	if (!x) return;
	if (!y) return;
	if (pos[x][y]) cout << x << ' ' << pos[x][y] << endl, print(x - 1, y - w[x] * pos[x][y]); 
	else print(x - 1, y);
}
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) cin >> c[i] >> w[i];
	for (int i = 1; i <= n; ++i) {
		for (int j = 0; j <= m; ++j) {
			f[i][j] = f[i - 1][j], pos[i][j] = 0;
			if (j >= w[i] && f[i][j] < f[i][j - w[i]] + c[i]) f[i][j] = f[i][j - w[i]] + c[i], pos[i][j] = pos[i][j - w[i]] + 1;
		}
	}
	cout << f[n][m] << endl;
	print(n, m);
	return 0;
} 

最后说一句:

本博客由打爆老瘟猪有限公司赞助

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值