问题简介
输入:n种物品和一个背包
物品i的重量是wi,价值为vi
背包的容量是C
输出:装入背包的物品
优化目标是:装入背包的物品总价值最大
优化子结构
设(x1,x2,x3…xn)是0-1背包问题的一个最优解。
如果x1=1,那么(x2,x3,x4,x5…xn)是以下子问题的最优解:
如果x1 =0:则 (x2, …, xn) 是以下子问题的最优解
递归关系
设m(i, j)为背包问题的最优值,这里背包容量为j,可选物品为i, i+1, …, n,
有如下递归关系:
如果i<n时
i=n时
优化子结构
表格举例
给出如下三个物品,背包容量为5:
构造下列表格:
其中m[i][j]表示放入0-i的物品,背包容量为j的价值。
现在考虑放入id为0的物品,因为物品重量为1,所以在背包容量为0时无法放入,其他情况下都可以放入物品0,所以表格变为:
第二行时,要考虑放入0,1两个物品了,所以需要用到我们的迭代公式,
例如m[1][0],此时的背包容量为0,则什么都放不下为0,
而m[1][1]时,j<w1(物品1的重量为2),所以此时m[1][1]=m[0][1]=6;
再例如m[1][2]时,j>w1,此时m[1][2]=max{m[0][2],m[0][0]+v2}=max{6,0+10}=10
按照这个理论挨个填写,得到结果如下:
此时的m[2][5]是最佳的价值,但是如何求出最优解呢?
我们回顾m[2][5]的得到过程,m[2][5]=m[1][2]+v2=m[0][0]+v1=v1+v2,因此最佳解为放入物品1和物品2。
具体在算法中如何实现呢?我们选择利用上述的二维数组进行逆推,从最大可装入价值处逆推。
最大可装入价值处容量为5,减去最后放入的物品2的重量,得到5-3=2,我们跳转到去掉物品2,容量为2处,也就是m[1][2]处,此时装入了物品1,而1的重量为2,去掉物品1,和容量2,剩下m[0][0],此时容量已经变为0,算法结束,也就是装入了物品1和物品2。
具体代码如下:
#include<iostream>
#include<stdio.h>
#define N 6 //物品的个数
#define W 21 //背包容量
int B[N][W] = { 0 };
int w[6] = { 0,2,3,4,5,9 }; //物品重量
int v[6] = { 0,3,4,5,8,10 };//物品价值
void knapsack()
{
int k; //第K个物品
int C; //背包剩余重量
//填表
for (k = 1; k < N; k++)
{
for (C = 1; C < W; C++)
{
if (w[k] > C) //第k件物品放不进去 此时背包的价值 = 判断完上一件物品之后背包的价值
{
B[k][C] = B[k - 1][C];
}
else
{
int value1 = B[k - 1][C - w[k]] + v[k]; //放入第k件物品后 背包总价值 = 先给这件物品留出空间,剩余的背包大小能装进的最大价值 + 这件物品的价值
int value2 = B[k - 1][C]; //不放入第k件物品 背包总价值 = 不用给这件物品留出空间,当前背包大小能装进的最大价值(就是判断完上一件物品之后背包的价值)
if (value1 > value2)
{
B[k][C] = value1;
}
else
{
B[k][C] = value2;
if (value1 < value2)printf("k=%d C=%d\n", k, C);
}
}
}
}
}
int main()
{
knapsack();
for (int i = 0; i < N; i++)
{
for (int j = 0; j < W; j++)
{
printf("%4d ", B[i][j]);
}
printf("\n\n");
}
system("pause");
}
(代码摘自:https://hanquan.blog.csdn.net/article/details/89456491)