题目
有 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
输出样例:
10
思路
对状态计算进行集合划分
f(i,j)划分为第i个物品选0个...选k个
f(i,j) = max(f(i-1,j), k*w[i] + f(i-1, j-k*v[i]))
朴素做法
枚举
f[i][j]的集合,取最大值
#include <iostream>
using namespace std;
const int N = 1010;
int f[N][N];
int v[N], w[N];
int main()
{
int n, m;
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++)
for(int k = 0; k*v[i] <= j; k++)
f[i][j] = max(f[i][j], k*w[i] + f[i-1][j-k*v[i]]);
cout << f[n][m] << endl;
return 0;
}
二维dp优化
状态计算:状态式子展开,优化掉第三层循环遍历
原始状态方程:
f[i][j] = max(f[i-1][j], w+f[i-1][j-v], 2w+f[i-1][j-2v], 3w+f[i-1][j-3v]...., k*w+f[i-1][j-k*v]);
而恰好f[i][j-v] = max( f[i-1][j-v], w+f[i-1][j-2v], 2w+f[i-1][j-3v]....,(k-1)*w+f[i-1][j-k*v]);
上下对应比较,下面式子都比上面式子少一个w
则f[i][j] = max(f[i-1][j], w[i] + f[i][j-v[i]);
#include <iostream>
using namespace std;
const int N = 1010;
int f[N][N];
int v[N], w[N];
/*
状态计算:状态式子展开,优化掉第三层循环遍历
原始状态方程:
f[i][j] = max(f[i-1][j], w+f[i-1][j-v], 2w+f[i-1][j-2v], 3w+f[i-1][j-3v]...., k*w+f[i-1][j-k*v]);
而恰好 f[i][j-v] = max( f[i-1][j-v], w+f[i-1][j-2v], 2w+f[i-1][j-3v]....,(k-1)*w+f[i-1][j-k*v]);
上下对应比较,下面式子都比上面式子少一个w
则 f[i][j] = max(f[i-1][j], w[i] + f[i][j-v[i]);
*/
int main()
{
int n, m;
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], w[i] + f[i][j-v[i]]);
}
cout << f[n][m] << endl;
return 0;
}
一维dp优化
从小到大枚举容量,该方式搭配滚动数组,执行时
max(f[j], w[i]+f[j-v[i]])
中的f[j]
是上一层的状态,相当于f[i-1][j]
而由于j-v[i] < j
,因此f[j-v[i]]
是当前层的状态,相当于f[i][j-v[i]]
#include <iostream>
using namespace std;
const int N = 1010;
int v[N], w[N];
int f[N];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
// 枚举所有状态
for(int i = 1; i <= n; i++)
// 从小到大枚举容量,该方式搭配滚动数组,执行时
// max(f[j], w[i]+f[j-v[i]])中的f[j]是上一层的状态,相当于f[i-1][j]
// 而由于j-v[i] < j,因此f[j-v[i]]是当前层的状态,相当于f[i][j-v[i]]
for(int j = v[i]; j <= m; j++)
f[j] = max(f[j], w[i]+f[j-v[i]]);
cout << f[m] << endl;
return 0;
}