0-1背包问题状态转移方程 f[i][j] = Max{f[i-1][j],f[i-1][j-w[i]]+v[i]}
其中 w[i] 为第i件物品的重量,v[i]为第i件物品的价值
f[i][j] 表示 从前 i 件物品中选出若干件放到承重为 j 的袋子中所能达到的最大价值
每件物品就只有两个状态:放与不放
为了使价值最大化 我们要考虑第i件物品放不放进袋子中(放与不放有两个影响:1、影响袋子的承重;2、影响袋子的总价值;所以不要觉得价值大就一定放,它可能消耗很大的承重而导致最终的结果不是最优)
为了便于理解 可以为 f[i][j] 建一个表格 动态规划的过程其实就是这张表的填表过程
现在假设袋子的承重为10,一共有五件物品,其中:
w[1-5] = {2,2,6,5,4} v[1-5] = {6,3,5,4,6} 求解袋子价值的最大值
表格:
w[1-5] = {2,2,6,5,4}
v[1-5] = {6,3,5,4,6}
1 2 3 4 5 6 7 8 9 10
1 0 6 6 6 6 6 6 6 6 6
2 0 6 6 9 9 9 9 9 9 9
3 0 6 6 9 9 9 9 11 11 14
4 0 6 6 9 9 9 10 11 13 14
5 0 6 6 9 9 12 12 15 15 15
表格是这么填的 比如先填第一行 [1,1] 就是现在可选的物品就第1件 袋子承重为1 最大价值则为0
即 [1,1] = 0 因为 这件物品重量为 2 不能放
以此类推 [1,2] 就是现在可选的物品就第一件 承重 为2 最大价值则为 6 现在可以放了 那么后面的
就全为6了 承重为 3-10 的当然可以放下这件重量为2的物品 所以它们的最大价值都为 6
第二行 [2,1] 就是现在可选的物品是第1、2件 袋子承重为 1 最大价值则为0,因为这两件物品重量
都大于1 [2,2] 还是可选前两件 但选哪件呢?还是都选?当然不能都选 都选重量为 4 > 2 只能选一件
就选价值为6的第一件 因为它的价值大于第2件的3 后面全部依次类推。
/*
有五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
解:15
*/
#include <iostream>
#include <cstdio>
#include <memory.h>
using namespace std;
int v[6] = {0,6,3,5,4,6}; //下标0不用
int w[6] = {0,2,2,6,5,4};
//int v[6] = {0,6,4,5,3,6};
//int w[6] = {0,4,5,6,2,2};
int f[6][11];
int flag[6][11];
/*flag[i][j] 表示 从 前i件物品中挑若干件物品到承重为j的袋子中 所得价值最大时
是否选择了 第 i 件物品 如果选了 则flag[i][j] = 1 否则为0 */
int main()
{
memset(f,0,sizeof(f));
memset(flag,0,sizeof(flag));
int n = 5;
for (int i = 1; i <= 5; i++)
//for (int j = 1; j <= 10; j++)
{
for (int j = 1; j <= 10; j++)
//for (int i = 1; i <= 5; i++)
{
//为了价值最大 考虑第i件物品放不放进袋子
if (j-w[i] < 0) //承重为j的袋子放不下第i件物品 肯定就不放了
{
f[i][j] = f[i-1][j];
}
else
{
//注释1
f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
if (max(f[i-1][j],f[i-1][j-w[i]]+v[i]) == f[i-1][j-w[i]]+v[i])
/*
如果价值较大为选了第i件物品的情况
置flag[i][j]为1
*/
flag[i][j] = 1;
}
}
}
//输出表格f[i][j]
for (int i = 1; i <= 5; i++)
{
for (int j = 1; j <= 10; j++)
{
cout << f[i][j] << " ";
}
cout << endl;
}
cout << endl;
//输出价值最大时所挑物品的各自重量
//注释2
for (int i = 5,j = 10; ; )
{
if (j - w[i] < 0) break;
//if (i <= 0) break;
if (flag[i][j]) //如果f[i][j] 为1
{
// 输出所选的第i件物品的重量w[i]
cout << w[i] << " ";
// 承重变为 j - w[i]
j = j - w[i];
// 可选物品除去第i件(减少一件)
i = i - 1;
}
else
//没选则承重不变 物品减少一件
i = i - 1;
}
cout << endl;
/*
注释2
要想知道 最大值为 f[5][10] = 15 时 袋子里装的是哪些物品 则这样思考
flag[5][10] = 1 表示 价值最大时 第 5 件物被装到了袋子里
那再考察 f[4][10-w[5]] 是不是等于1?如果是 则第4件也被装进袋子里
否则没装 没装就继续考察 f[3][10-w[5]] (不用减去w[4])。。。
就这样一直往回推就行
*/
return 0;
}
<span style="font-family: KaiTi_GB2312;font-size:18px;"></span><pre name="code" class="cpp"> /*注释1:
如果承重为j的袋子可以放下第i件物品 即 j>= w[i]
那么除去w[i]的重量 袋子剩下的承重 j-w[i] 去装
前i-1件物品 可以 使袋子最大价值是多少? 再加上
第i物品的总价值是多少? 与不放第i件物品的总价值
f[i-1][j]相比 哪个大? 选择较大者。 (如果给了第
i件物品而又不放 那就跟没有一样 相当于就是从i-1件
物品挑若干件放到承重为j的袋子中 使价值最大)
*/
/*
假设 i = 4 , j = 10
前 3 件物品重量分别为 5 4 3
价值分别为 3 5 1
第 4 件物品重量为 5
价值为 7
现在考察 f[4][10] 的最大值
首先当承重为 10 时 从前三件挑 不能全挑 否则重量为 12 超过载重
则挑前两件 重量总和为 9 价值达到 8
而 10 > 5 即袋子可以装 第4件物品 那么如果放第i件物品 则袋子只有
剩下 的 5 单位承重去装 前三件物品 现在挑的话 只能 挑其中一件 挑
两件以上都会超重 则挑价值最大的 第2件 重量为 4 价值为 5
那么加上 第4件物品 总重为9 不超过承重 价值达到 5+7 = 12 > 8
所以当然要选择放第4件物品
如果 第4件物品 重量 大于 10 无论它价值多高都不能放 跟没给一样
则 f[i][j] 就只能等于 f[i-1][j]
*/