前言
没有接触过01背包的不推荐先搞别的背包问题,思路可能会不清楚或绕在一起,效果反而不好,建议先理解透彻01背包:01背包问题
完全背包问题
样题:完全背包问题
先来分析一下完全背包问题与01背包问题的相同点和不同点:
01背包 | 完全背包 |
---|---|
每件物品只有 1 1 1件 | 每种物品有 ∞ \infty ∞(无限,不懂数学的小伙伴们要加油了)件(并不是真的有 ∞ \infty ∞件,后面会讲到代码中的上限) |
每件物品有两个属性:重量 w w w和价值 c c c | 每种物品有两个属性:重量 w w w和价值 c c c |
递推方式:逆推 | 递推方式:顺推,以后会讲为什么 |
思路
好啦,那么我们从这里开始讲解思路。
首先是输入和初始化部分 (只要有手就行),这部分我就不多做赘述了。
之后是重点。
核心代码思路(朴素版)
这个其实就是吧原本的完全背包换成了01背包来求解。
首先我们来讨论在上文表格中说的 “并不是真的有
∞
\infty
∞件”。事实也确实是这样,它是有上限和下限的。
下限我们都很清楚,就是
0
0
0。
上限呢?
It depends.(这要看情况而定)
好了,有点扯远了( ̄ー ̄)。其实上限有一个限制条件:那就是物品总重量不超过
j
j
j,用条件语句写出来就是:
k
×
w
[
i
]
≤
j
k\times w[i]\le j
k×w[i]≤j,这里的
k
k
k指当前物品的件数,由下限一直增加到上限。
循环结构
// 外面的东西
for (int k = 0; k * w[i] <= j; ++k) {
// 状态转移方程
}
// 输出答案
状态转移方程
跟01背包没啥区别,但是要注意减去的重量是
k
k
k倍的,增加的价值也是
k
k
k倍的。
状态转移方程如下:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
−
1
]
[
j
−
k
×
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[i][j]=max(dp[i−1][j],dp[i−1][j−k×w[i]]+k×c[i])
代码
#include <iostream>
using namespace std;
int main()
{
int n, m;
cin >> m >> n;
const int N = n + 1, M = m + 1;
int w[N], c[N], dp[N][M];
for (int i = 1; i < N; ++i) cin >> w[i] >> c[i];
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
dp[i][j] = 0;
for (int i = 1; i < N; ++i)
for (int j = m; j >= 1; --j)
for (int k = 0; k * w[i] <= j; ++k)
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - k * w[i]] + k * c[i]);
cout << dp[n][m];
}
核心代码思路(优化版)
压缩数组
这次我们还是通过压缩数组和状态转移方程来优化。
实际上我们可以顺着递推,也就是需要覆盖数据。
为什么呢?因为我们希望放的东西累加起来,所以不能使用旧数据,而是使用累加过的新数据。
这样就完成了压缩数组。
状态转移方程式(优化版)
状态转移方程如下:
d
p
[
j
]
=
m
a
x
(
d
p
[
j
]
,
d
p
[
j
−
w
[
i
]
]
+
c
[
i
]
)
dp[j]=max(dp[j],dp[j-w[i]]+c[i])
dp[j]=max(dp[j],dp[j−w[i]]+c[i])
是的,你没有看错!
这就是妥妥的01背包状态转移方程!!!
可是注意区别:
01背包 | 完全背包 |
---|---|
逆推,使用旧数据,不会累加价值 | 顺推,使用新数据,会累加价值 |
代码(优化版)
#include <iostream>
using namespace std;
int main()
{
int n, m;
cin >> m >> n; //size, numbers of things
const int N = n + 1, M = m + 1;
int w[N], c[N], dp[M];
for (int i = 1; i < N; ++i) cin >> w[i] >> c[i];
for (int i = 0; i < M; ++i) dp[i] = 0;
for (int i = 1; i < N; ++i)
for (int j = w[i]; j <= m; ++j) dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
cout << "max=" << dp[m];
}
世界因为代码变短、变有用、变漂亮、变得能够输出正确答案而明亮起来 (*´゚∀゚`)ノ
有如果任何问题或建议欢迎在评论区指出(~ ̄▽ ̄)~