01背包问题
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0 < N, V <= 1000
0 < vi, wi <= 1000
Example 1:
Input:
4 5
1 2
2 4
3 4
4 5
Output:
8
题解思路 DP
我们定义
f[i][j] 表示从前i个物品中,选择总体积不超过j的所有物品集合的最大价值
那么
1.当我们不选择物品i时,
f[i][j] = f[i - 1][j]
2.当我们选择物品i时
f[i][j] = f[i -1][j - v[i]] + w[i]
综上可得,
f[i][j] = max(f[i -1][j], f[i -1][j - v[i]] + w[i])
那么,我们最终要求得的问题最终解即f[n][v]。
根据状态计算,写出代码。
code
#include <stdio.h>
#include <algorithm>
using namespace std;
int main()
{
const int N = 1010;
int f[N][N] = {0};
int n = 0;
int m = 0;
int v[N] = {0};
int w[N] = {0};
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d %d", &v[i], &w[i]);
for (int i = 1; i <= n; ++i) {
for (int j = 1; 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]);
}
}
printf("%d\n", f[n][m]);
return 0;
}
代码优化
观察上述代码,我们不难发现,第一维数组f[i],只和f[i -1]相关,在遍历i的过程中,我们必先会求得f[i - 1],故可以做等价变换,将i这个维度去掉,简化代码如下
#include <stdio.h>
#include <algorithm>
using namespace std;
int main()
{
const int N = 1010;
int f[N] = {0};
int n = 0;
int m = 0;
int v[N] = {0};
int w[N] = {0};
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d %d", &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]);
}
}
printf("%d\n", f[m]);
return 0;
}