01背包空间优化,完全背包时间优化 的图解

01背包空间优化

当初学01背包的时候,会有这样一个状态转移方程:

约定:
dp[i][j]表示在0~i下标物品中选取,在总金额不超过j的情况下获得的最大价值
v[i]表示第 i 下标的物品的价值

dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]]+v[i]);

dp[i-1][j] 对应不选 【第 i 下标】 的物品
dp[i-1][j-v[i]]+v[i] 对应选 【第 i 下标】 的物品

可以很快写出递推的式子

maxPrice表示考虑的最大总金额
共有 0~n-1 下标共n件物品

for(int i=0; i<n; i++)
	for(int j=0; j<=maxPrice; j++)
		dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]]+v[i]);

可以发现,当前格子的答案,只和上一行0~j下标的格子有关
在这里插入图片描述
那么我们只存一行就好了啊,而且问题来自于上一行 j 以及前面的区域,我们必须让 j 以递减的形式更新,以保证能够取到上一行的前面的值(因为 j 递减更新的话前面是旧值,我们恰恰需要上一行的旧值)

如图:蓝色表现新更新的值,红色表示上一行的旧值,即考虑下标 0~i-1的情况
在这里插入图片描述
于是可以在空间上简化状态转移

for(int i=0; i<n; i++)
	for(int j=maxPrice; j>=0; j--)
		dp[j] = max(dp[j], dp[j-v[i]]+v[i]);

完全背包时间优化

完全背包和01背包类似,但是每件物品可以选无数次,那么还是可以基于01背包的基础上快速写出状态转移

约定:
dp[i][j]表示在0~i下标物品中选取,在总金额不超过j的情况下获得的最大价值
共有 0~n-1 下标共n件物品
maxPrice表示考虑的最大总金额
v[i]表示第 i 下标的物品的价值
k表示第i下标的物品,选取k件

for(int i=0; i<n; i++)
	for(int j=0; j<=maxPrice; j++)
		for(int k=0; j-k*v[i]>=0; k++)
			dp[i][j] = max(dp[i-1][j], dp[i-1][j-k*v[i]]+k*v[i]);

枚举第i下标物品选取的可能,然后做多次01背包取最大结果,可是这样带来的是时间的开销增加,有没有办法去掉k的循环?

【在0~i下标物品中选取,在总金额不超过 j 的情况下获得的最大价值】,其实问题可以这么想:

同一件物品,我们可以取无数次,那么问题转化为:

  1. 【在0~i-1下标物品中选取,在总金额不超过 j 的情况下获得的最大价值】
  2. 【在0~i下标物品中选取,在总金额不超过 j-v[i] 的情况下获得的最大价值】,即尝试多选一件

问题1是上一行的值,没什么问题,问题2,是这一行右边的值,因为 j 是递增更新的,我们很快发现,问题2的最优答案我们刚刚已经做过了,就在这一行的左边,那么我们很快能够做出优化

假设问题1的解是红色区域,问题2的解是黄色区域
在这里插入图片描述

约定:
dp[i][j]表示在0~i下标物品中选取,在总金额不超过j的情况下获得的最大价值
共有 0~n-1 下标共n件物品
maxPrice表示考虑的最大总金额
v[i]表示第 i 下标的物品的价值

for(int i=0; i<n; i++)
	for(int j=0; j<=maxPrice; j++)
		dp[i][j] = max(dp[i-1][j], dp[i][j-v[i]]+v[i]);

如何枚举k?即如何确保选取多件物品?
dp[i][j-v[i]]的值,由dp数组的定义,已经是考虑选取多件 i 物品的结果了,而我们要做的就是试图增加一件 i 物品,基于已经最优的答案(可能选了k件),尝试增加一件 i 物品,看看是否会得到更大的结果

同样可以基于01背包的空间优化,这里给出空间优化的代码,而且这个代码和01背包的空间优化非常相似,只是 j 循环的方向发送改变

for(int i=0; i<n; i++)
	for(int j=0; j<=maxPrice; j++)
		dp[j] = max(dp[j], dp[j-v[i]]+v[i]);
发布了262 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44176696/article/details/105209974
今日推荐