0-1背包参考数据
- 第一行代表n个物品和背包容量c
- 第二行代表每个物品的重量 w i w_i wi
- 第三行代表每个物品的价值 v i v_i vi
4 9
2 3 4 5
2 4 5 7
计算机算法分析和设计第五版书中这样描述
m
(
i
,
j
)
=
m
a
x
(
m
(
i
+
1
,
j
)
,
m
(
i
+
1
,
j
−
w
i
)
+
v
i
)
当
j
>
=
w
i
m(i,j) = max(m(i + 1, j), m(i + 1, j - w_i) + v_i) 当 j >=w_i
m(i,j)=max(m(i+1,j),m(i+1,j−wi)+vi)当j>=wi
或者
m
(
i
,
j
)
=
m
(
i
+
1
,
j
)
当
0
<
=
j
<
w
i
m(i, j) = m(i + 1, j) 当 0 <= j < w_i
m(i,j)=m(i+1,j)当0<=j<wi
- 我们假设这里有一个表格,行是n,列是c;我们从后往前填(倒思路)
- 开始我们选择最后一个物品,如果最后一个物品重量小于c,这里最后一个物品重量是5,小于c=9,我们设定,w[n]-1无法放入,下图黄色区域,剩余部分可以放入。
- 我们继续从n - 1个物品开始考虑之前的物品,按照公式,我们可以把第三行表格分为两个部分,一个部分无法放入新物品,一个部分可以放入。先填入无法放入的区域,价值为m(i, j) = m(i+1, j)。即表示第三行和第四行数据相同。如下图绿色部分,之后从最右边往左开始填数字,即从9开始天道第四列,这些都可可能产生最新价值的部分
m
(
i
,
j
)
=
m
a
x
(
m
(
i
+
1
,
j
)
,
m
i
+
1
,
j
−
w
i
)
+
v
i
m(i, j) = max(m(i+1, j), mi+1, j-w_i) + v_i
m(i,j)=max(m(i+1,j),mi+1,j−wi)+vi
- 继续按照步骤,直到第一行
- 最后一行单独考虑,不用计算全部,只需要考虑能不能放下最后一个物品,最终的表格如下,非常巧妙。
- 参考代码
#include <algorithm>
#include <iostream>
using namespace std;
int track(int v[], int w[], int c, int n, int m[][100])
{
int Jmax = min(w[n] - 1, c);
/*
* 1. Jmax如果等于w[n] - 1 说明,第n件物品无法放下,前[0, w[n]-1]的重量区间可装载0
* 2. Jmax如果为c,说明第n件物品比c还重,因此[0,c]区间重量可装载0
*/
for (int j = 0; j <= Jmax; j++) {
m[n][j] = 0;
}
// 如果可以装下第n件物品,则第n栏,该重量位置价值为v[n]
for (int j = w[n]; j <=c; j++)
m[n][j] = v[n];
for (int i = n - 1; i > 1; i--) {
Jmax = min(w[i] - 1, c);
/*
* 1. Jmax如果等于w[i] - 1 说明,第n件物品无法放下,前[0, w[n]-1]的重量区间可装载重量不变,和之前相同
* 2. Jmax如果为c,说明第n件物品比c还重,因此[0,c]区间重量可装载不变,和之前相同
*/
for (int j = 0; j <= Jmax; j++) {
m[i][j] = m[i+1][j];
}
// 如果可以放下,物品的价值大小取决于max(之前大小,去除w[i]件物品可以放下的最大重量+当前w[i]重量)
for (int j = w[i]; j <=c; j++) {
m[i][j] = max(m[i+1][j], m[i+1][j-w[i]] + v[i]);
}
// 是否能放下最后一个物品
m[1][c] = m[2][c];
if (c >= w[1]) {
m[1][c] = max(m[1][c], m[2][c-w[1]] + v[1]);
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <=c; ++j) {
cout << m[i][j] << " ";
}
cout << endl;
}
return m[1][c];
}
int main()
{
int n = 4;
int c = 9;
int w[5] = {0, 2, 3, 4, 5};
int v[5] = {0, 3, 4, 5, 7};
int m[100][100] = {0};
cout << track(v, w, c, n, m) << endl;
return 0;
}
结果:
0 0 0 0 0 0 0 0 12
0 0 4 5 7 7 9 11 12
0 0 0 5 7 7 7 7 12
0 0 0 0 7 7 7 7 7
12
这个和正序的有什么区别,这里列出一个表格,正序的代码简单一点,思考简单一点。第一次遇到书本写01-背包问题,发现和当初竞赛的学习以及以前的知识有点不同,因此记录了一下:
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= m; j++)
{
// 当前背包容量装不进第i个物品,则价值等于前i-1个物品
m[i][j] = m[i - 1][j];
// 能装,需进行决策是否选择第i个物品
if(j>=v[i])
m[i][j] = max(m[i][j], m[i - 1][j - v[i]] + w[i]);
}
}