关于动态规划的背包问题,可分为
(1)0/1背包问题
(2)分组背包问题
(3)多重背包问题
(4)完全背包问题
(1)0/1背包问题。
问题描述:有NN件物品和一个容量是 VV的背包。每件物品只能使用一次。
第 ii件物品的体积是 vivi,价值是wi wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输入描述:第一行两个整数,N,V 用空格隔开,分别表示物品数量和背包容积。
接下来有N行,每行两个数vi,wi,用空格分隔开,表示该物体的体积和价值。
N,V
输出描述: 输出一个整数,表示最大价值。
分析
引入一个(N+1)*(V+1)的二维数组dp[ ][ ]。
dp[i][j]表示把前i个物品【1,i】装入容量为j的背包中获得最大价值
把每个dp[i][j]都看做一个背包;容量为j,装1~i这些物品,最后的dp[N][V]为问题答案。
dp转移方程:
(1)、第i个物品的体积比容量j还大,不能装进背包。直接继承前 i-1 个物品装进容量为j的背包情况即可,即dp[i][j]=dp[i-1][j]。
(2)、第i个物品的体积比容量j小,能装进背包,又可以分成两种情况:装或不装:
①装第i个物品。从前i-1个物品推广,前i-1个物品价值为dp[i-1][j]。第i个背包装进背包后,背包容量减少v[i],价值增加w[i]
有dp[i][j]=dp[i-1][j-v[i]]+w[i]。
②不装第i个物品,有dp[i][j]=dp[i-1][j]。
取两者的最大值:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])
递推代码(暴力做法):
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int w[N], v[N]; //物品的价值和体积
int dp[N][N];
int solve(int n, int V) {
for (int i = 1; i <= n; i++)
for (int j = 0; j <= V; j++) {
if (v[i] > j)
dp[i][j] = dp[i - 1][j]; //第i个物品比背包大,装不了。
else
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]); //第i个物品能装。
}
return dp[n][V];
}
int main() {
int n, V;
scanf("%d%d", &n, &V);
for (int i = 1; i <= n; i++)
scanf("%d%d", &v[i], &w[i]);
printf("%d", solve(n, V));
return 0;
}
记忆化搜索(暴力做法):
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int w[N], v[N];
int dp[N][N];
int solve(int i, int j) {
if (dp[i][j] != 0)
return dp[i][j];
if (i == 0)
return 0;
int res;
if (v[i] > j)
res = solve(i - 1, j);
else
res = max(solve(i - 1, j), solve(i - 1, j - v[i]) + w[i]);
return dp[i][j] = res;
}
int main() {
int n, V;
scanf("%d%d", &n, &V);
for (int i = 1; i <= n; i++)
scanf("%d%d", &v[i], &w[i]);