问题描述
有N件物品和一个容量是V的背包,每件物品只能使用一次,第i件物品的体积是vi,价值是wi,求解将哪些物品装入背包可以使物品的总体积不超过背包容量,且总价值最大,输出最大价值。
输入格式及输出格式
输入格式:第一行两个整数,表示物品数量和背包容积,接下来有N行,没行两个整数vi和wi,用空格分开,分别表示第i件物品的体积和价值。
输出格式:输出一个整数,表示最大价值
基本思想
首先,我们可以设置一个f[i][j]用来表示,物品遍历到第i个时,容量为j的最大价值(物品可能放了也可能会没放),设置两重循环,第一层用来遍历物品,第二层遍历容量,将每一层的f[i][j]初始化赋值为f[i-1][j],表示初始化的情况为不拿这一层的东西。当j大于v[i]时,说明当前容量可以取得这个物品,此时,我们就需要与不拿的情况做比较取最大值,于是可得基本的动态规划方程:f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i] + w[j])
代码实现
由上述分析,可得代码如下
#include <iostream>
using namespace std;
const int N = 10010;
int n, m;
int v[N], w[N];
int 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 - 1][j], f[i - 1][j - v[i]] + w[i]);
}
cout << f[n][m] << endl;
return 0;
}
代码优化
细心的朋友可能会发现,我们每一次的计算,只是在前一层的基础上进行覆盖,你现在要计算f[i][j]的值,实际上可以用f[i - 1][j]暂存,可以省下一部分空间,所以第一时间我们会想到,优化代码为:
for (int i = 1; i <= n; i ++ )
for (int j = v[i]; j <= m; j ++ )
f[j] = max(f[j], f[j - v[i]] + w[i]);
以上代码真的正确吗?当然不是,当我们选择以上形式的话,f[j - v[i]]其实相当于f[i][j - v[i]],这显然是与f[i - 1][j - v[i]]是不符的,换句话说,我们在动态规划的过程中,改变了我们这一层滑动数组的值,如何避免?我们可以选择从前往后进行遍历优化,即:从j容量开始,一直减到v[i],于是,我们可以得到以下的代码:
#include <iostream>
using namespace std;
const int N = 10010;
int n, m;
int v[N], w[N];
int 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;
}
任何错误,欢迎指正,Thanks♪(・ω・)ノ