有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
推导分析:
v 为体积 w为价值 f[ i ] [ j ] 表示在前 i 个物品里选择的总体积小于等于 j 时的最大价值
将物品分为两个集合 : S1( 含有第 i 个物品 ) S2( 不含有第 i 个物品 )
S1 的表达式为 f [ i - 1 ] [ j ] S2的表达式为 f [ i -1 ] [ j - v [ i ] ] + w [ i ]
那么f [ i ] [ j ] = max ( f [ i - 1 ] [ j ] , f [ i -1 ] [ j - v [ i ] ] + w [ i ] )
弄明白关系后这个式子就是动态规划的状态转移方程 以上是二维方程
做出二维之后就可以考虑优化为一维
为什么可以转为一维
首先观察状态转移方程 dp[i][j]是由 dp[i-1][jxxxx]推导而来,仅看第一个维度,即i - 1 与 i ,可以发现第i层是由上一层推导而来的。
故我们不必要保存i - 2 层,比如我们计算第三层是只需要第二层的。不需要第一层的数据。
当我们去掉i时,即我们不需要控制第几层,只需要长度为j的数组,保存确认过最新的一层。作为下一层的参考。例如我们计算第三层dp时,此时dp原数据保存的是第二层的结果。
为什么要逆序
首先,通过上一个问题,我们确认了我们目前一维的dp数组,保存的是确认过的最新一层的数据,即上一层的数据。
当我们计算当前层时,对于二维时的状态转移方程有
dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);
可以看到,dp[i - 1][j - v[i]] + w[i] 使用的上一层的原始数据(dp[i - 1]),而我们使用一维的状态转移方程时有
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
当我们从小到大更新是, 因为j - v[i] 是严格小于j 的,所以我们可以举个例子 dp[3] = max(dp[3], dp[2] + 1); 因为我们是从小到大更新的,所以当更新到dp[3]的时候,dp[2]已经更新过了,已经不是上一层的dp[2]。
而当我们逆序更新时有,举例 dp[8] = max(dp[8], dp[6] + 2)当更新dp[8]时,dp[6]还没有被更新,还是上一层的数据,这样才能保证没有读入脏数据。
作者:断然
链接:https://www.acwing.com/solution/content/116859/
//二维背包
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e3+10;;
int n , m ;
int v[N] , w[N] , f[N][N] ;
int main()
{
cin >> n >> m ;
for(int i = 1 ; i <= n ; i++){
cin >> v[i] >> w[i] ;
}
for(int i = 1 ; i <= n ; i ++ ){
for(int j = 0 ; j <= m ; j ++ ){
f[i][j] = f[i-1][j] ;
if( j >= v[i] ) f[i][j] = max( f[i][j] , f[i-1][j - v[i] ] + w[i] );
}
}
cout << f[n][m] << endl;
return 0;
}
//一维优化
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e3+10;;
int n , m ;
int v[N] , w[N] , f[N] ;
int main()
{
cin >> n >> m ;
for(int i = 1 ; i <= n ; i++){
cin >> v[i] >> w[i] ;
}
for(int i = 1 ; i <= n ; i ++ ){
for(int j = m ; j >= v[i] ; j -- ){ //改为从大到小
f[j] = max( f[j] , f[j - v[i] ] + w[i] );
}
}
cout << f[m] << endl;
return 0;
}