一、问题描述
给定n种物品和一个背包,物品i(1≤i≤n)的重量是wi,其价值为vi,背包的容量为C。对每种物品只有两种选择:装入背包或者不装入背包。如何选择装入背包的物品,使得装入背包中物品的总价值最大?
二、分析与求解
这是一个经典的动态规划问题。容易证明,其满足最优子结构性质:即设(x1, x2, ..., xn)是该问题的一个最优解(其中xi=1表示放入物品i,xi=0表示不放入),则(x1, x2, ..., xk-1, xk+1, ..., xn)一定是原问题去除物品k后得到的子问题的最优解。
2.1 0/1背包问题
首先我们看一下背包问题的一个特例:0/1背包问题。(例题见于:Openjudge-0/1背包问题)
在此问题中,每种物品只有一件。于是,对于第i件物品,只有两种选择:放入背包或者不放入。最优解的递推表达式:
其中fk(y)表示背包容量为y且只能取前k种物品的条件下,可以放入的物品的最大总价值。
为了在求得最大总价值后,找到对应的最优解,还需要标记函数。令标记函数Ik(y)表示背包容量为y且只能取前k种物品的条件下,放入的物品的最大序号,则有
于是,我们可以写出0/1背包问题的代码:
『C++』
#include <iostream> #include <algorithm> using namespace std; #define maxn 200 #define maxw 200 int n, C; void TraceBack(int (*I)[maxn], int *w, int W) { int x[maxn] = {}; int y = W, k = n; while (I[k][y] != 0) { while (I[k][y] == k) { y -= w[k]; x[k]++; } k = I[k][y]; } for (int i = 1; i <= n; i++) printf("%d\n", x[i]); return; } int main() { while (cin >> n >> C) { int w[maxn] = {}, v[maxn] = {}; int F[maxn][maxw] = {}; int I[maxn][maxw] = {}; int W[maxn][maxn] = {}; for (int k = 1; k <= n; k++) { cin >> w[k] >> v[k]; } for (int k = 1; k <= n; k++) { for (int y = 0; y <= C; y++) { if (y >= w[k] && F[k - 1][y] < F[k - 1][y - w[k]] + v[k]) { F[k][y] = F[k - 1][y - w[k]] + v[k]; W[k][y] = W[k - 1][y - w[k]] + w[k]; I[k][y] = k; } else { F[k][y] = F[k - 1][y]; W[k][y] = W[k - 1][y]; I[k][y] = I[k - 1][y]; } } } printf("%d\n", F[n][C]); TraceBack(I, w, W[n][C]); } //system("pause"); return 0; }
2.2 背包问题
若同一种物品可以放入多个,则标记函数同上,而只需将递推方程改为如下即可: