经典的0-1背包问题-滚动数组优化
背包问题是一个经典的问题,除此之外还有其扩展的变形。
下面,我们把背包问题仅仅当做一个问题来解决,而不太多的去思考动态规划这个笼统的概念。
下面我会把这个经典的最原始的背包问题的算法思路详细的表述出来,并从二维数组到一维数组优化(空间优化)
注:(二维数组到一维数组的空间优化方案简称为滚动数组。)
注:(首先,要大致了解解决背包问题的动态规划思想,但是懂得解决背包问题的动态规划思想未必会举一反三,在解决背包问题的时候,我避免了去思考动态规划这个笼统的概念,以防止搞不清楚动态规划到底是什么。)
算法思路
这是物品的重量和价值。
物品 | 重量w | 价值v |
---|---|---|
a | 2 | 6 |
b | 2 | 3 |
c | 6 | 5 |
d | 5 | 4 |
e | 4 | 6 |
分析:什么情况下给定的背包容量会装入最大价值?
对物品一个个进行选择。
选择 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
对第1个物品选择 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
对第2个物品选择 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
对第3个物品选择 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 11 | 11 | 14 |
对第4个物品选择 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 10 | 11 | 13 | 14 |
对第5个物品选择 | 0 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
上述表格中的数据的含义:当对第i个物品进行选择,背包容量为j时所容纳的最大价值。
dp[i][j]代表对第i个物品进行选择,背包容量为j时所容纳的最大价值。
dp[i-1][j]代表对第i-1个物品进行选择,背包容量为j时所容纳的最大价值。
dp[i-1][j-w[i]]代表对第ii物品进行选择,背包容量为j-w[i]时所容纳的最大价值。
而通过上面表格的分析,可得出一个状态方程:dp[i][j] = max{dp[i-1][j], v[i] + dp[i-1][j-w[i] }
由分析发现,当准备更新dp[i][j]的值的时候,dp[i-1][j]和dp[i-1][j-w[i]]的值会被取出使用,之后会填入dp[i][j]中,当对第i个物品选择结束后,第i-1个物品的空间就没用了,所以如果将状态方程转化为:dp[j] = max{dp[j], v[i] + dp[j-w[i]]} 就实现了空间的优化。
注(如果没看懂我上面那段话,就不要懒于分析了,动手分析一下,画个如上图的二维表格,一步一步进行分析。)
源代码
一维滚动数组优化。
背包容量的for循环倒着。
#include <iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int dp[50001],w[10001],v[10001],W,n;
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
memset(dp, 0, sizeof(dp));
scanf("%d%d", &n,&W);
for (int i = 1; i <= n; i++)
scanf("%d%d", &w[i], &v[i]);
//对第i个物品进行选择,从大于第i个物品的背包承重量开始,进行计算。之所以使for (int j = W; j >=w[i]; j--) j倒着来,是因为一维数组要进行数据覆盖。
for (int i = 1; i <=n; i++)
for (int j = W; j >=w[i]; j--)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
cout <<dp[W] << endl;
return 0;
}