学习使用动态规划解决0-1背包问题,体会动态规划算法自底向上的计算过程。
问题描述
给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?(与完全背包问题的区别在于:完全背包问题的物品是可部分装入,但是0-1背包问题的物品是不能部分装入的)
其数学表达如下:
xi表示第i的物品的选中情况,当xi=1时,该物品被选中,vixi=物品的重量;当xi=0时,表示物品未选中,vixi=0;
最优子结构性质
递归关系的分析
状态转移方程的创建
根据上面的递归关系可以创建如下的状态转移方程:
1. m[i][0]=0,0=<i<=n (n是物品的数量,背包的容量是零,不能装入任何物品,总价值为零) 边界条件
2. m[0][j]=0,0=<j<=c(w是背包的容量,当前没有物品可装入,总价值为零) 边界条件
3. m[i][j]=m[i-1][j] 当j<w[i]时,物品放不下
4. m[i][j]=MAX{
m[i-1][j],m[i-1][j-w[i]+v[i]} 物品可以放得下的情况,要比较不包含当前物品背包的总价值和包含当前物品的背包的总价值的大小。
计算包含当前物品的背包总价值的方式:m[当前的物品编号-1][当前背包的容量-当前物品的重量]定位到不包含当前物品的的背包剩余容量的最大价值,最后再加上当前物品的价值。
可以通过以下表格理解该状态方程:
通过表格可以得出计算顺序是:
1.边界值赋值
2.比较j和w[i]
3.比较m[i-1][j]和m[i-1][j-w[i]+v[i]的大小
算法代码
1.c语言代码
int x[4];
//动态规划法求解0-1背包问题,n表示物品的数量,c表示背包的容量
int knapsack(int n, int c) {
//边界值赋值
for (int i = 0; i < n + 1; i++)
for (int j = 0; j < c + 1; j++) m[i][j] = 0;
//从1开始,注意边界条件
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < c + 1; j++) {
//因为物品的编号是从1开始的,但是在数组中1号物品的重量存放在w[0]中,所以在表示重量时i要减去1
if (j <= w[i-1]) {
m[i][j] = m[i - 1][j];
}
else
{
//因为物品的编号是从1开始的,但是在数组中1号物品的价值存放在w[0]中,所以在表示价值时i要减去1
int t = m[i - 1][j - w[i-1]] + v[i-1];
if (m[i - 1][j] > t) {
m[i][j] = m[i - 1][j];
}
else
{
m[i][j]