0-1背包问题
背包问题是一个非常经典的问题,今天我们就来讨论其中的一种:0-1背包问题
1. 问题描述
给出n个物品的体积A[i]和其价值V[i],将他们装入一个大小为m的背包,最多能装入的总价值有多大?
为什么叫0-1背包问题呢,题目中假设每一种背包只有装和不装两种情况,我们不可能装入两个A[0]的物品,所以叫0-1背包问题
2. 转移方程
我们构造一个二维的转移数组dp[i][j]
,表示前面i
个物品放入总体积为j
的背包中所获得的最大价值,那么:1
2dp[i][j] = max(dp[i-1][j], dp[i-1][j-A[i]] + V[i]);
// dp[i-1][j]表示不放入物品A[i], dp[i-1][j-A[i]] + V[i]表示放入物品A[i]所得到的最大价值
由于实际编程中物品的下标是从0
开始的,我们对上面的方程做一下修改1
dp[i+1][j] = max(dp[i][j], dp[i][j-A[i]] + V[i]);
那么程序就是1
2
3
4
5
6
7
8
9
10
11
12int backPack(int m, vector<int> A, vector<int> V) {
vector<vector<int> > dp(A.size()+1, vector<int>(m+1, 0));
for (int i = 0; i < A.size(); i++) {
for (int j = 0; j <std::min(A[i], m+1); j++) {
dp[i+1][j] = dp[i][j];
}
for (int j = A[i]; j <= m; j++) {
dp[i+1][j] = std::max(dp[i][j], dp[i][j-A[i]] + V[i]);
}
}
return dp[A.size()][m];
}
如果觉得i+1
比较难受,也可以这么写:1
2
3
4
5
6
7
8
9
10
11
12int backPack(int m, vector<int> A, vector<int> V) {
vector<vector<int> > dp(A.size()+1, vector<int>(m+1, 0));
for (int i = 1; i <= A.size(); i++) {
for (int j = 0; j <std::min(A[i], m+1); j++) {
dp[i][j] = dp[i-1][j];
}
for (int j = A[i]; j <= m; j++) {
dp[i][j] = std::max(dp[i-1][j], dp[i-1][j-A[i]] + V[i]);
}
}
return dp[A.size()][m];
}
3. 优化
3.1 二维变一维
从转移方程可以看到,实际当前i
的所有状态都只依赖于i-1
,所以上面的二维数组很容易优化到一维:1
2
3
4
5
6
7
8
9
10
11
12int backPack(int m, vector<int> A, vector<int> V) {
// write your code here
vector<int> dp(m+1, 0);
for (int i = 0; i < A.size(); i++) {
vector<int> new_dp = dp;
for (int j = A[i]; j <= m; j++) {
new_dp[j] = std::max(dp[j], dp[j-A[i]] + V[i]);
}
dp = new_dp;
}
return dp[m];
}
3.2 再优化
虽然上面的程序已经优化到一维了,但同时维护着两个一维数组,我们也已经说过当前i
的所有状态都只依赖于i-1
,那么我们是否可以只用一个一维数组呢,答案是肯定的,如下:1
2
3
4
5
6
7
8
9
10int backPack(int m, vector<int> A, vector<int> V) {
// write your code here
vector<int> dp(m+1, 0);
for (int i = 0; i < A.size(); i++) {
for (int j = m; j >= A[i]; j--) {
dp[j] = std::max(dp[j], dp[j-A[i]] + V[i]);
}
}
return dp[m];
}
这里要注意一个问题我们的for (int j = m; j >= A[i]; j--)
是反着写的,正着写是有问题的,如果j从小到大的话,dp[j-A[i]]
可能已经被更新过了,而我们需要的是前一轮的dp[j-A[i]]
,而反着写就没什么问题。
我另外有一篇文章完全背包问题
,有兴趣的读者可以去查看一下!