1.引入问题
有5个不同价值不同体积的物品,求在背包容量为13时能获得的最大价值?
物品价值:p[ ] = [18, 20, 15, 10, 3]
物品体积:v[ ] = [ 3, 8, 5, 2, 2]
定义:i :有前i个物品可取 //i = 0 开始 j : 背包容量
2.分析问题
背包问题是动态规划思想的典型问题,那么如何在不超过背包容量的同时保证物品总价值最大呢?首先我们可以为五种物品设置0~4 编号,按序取物品,每种物品只有两种状态: 装入背包与不装入背包。以本题为例,从后向前推理,前5个物品在限制容量的情况下的最大价值为只取前四个物品的最大价值(不取第五个)和前四个物品在保留第五个物品空间下的最大价值+第五个物品的最大价值(取第五个)如此递推… 如下:
f(x,y) 表示前x个物品在背包容量为y时的最大价值 则: f(5,13) = max(f(5-1,13),f(5-1,13-2)+3) f(4,13) = ... f(4,11) = ... ...
动态规划解题三部曲:
-
定义子问题:
F(i, j) : 只取前 i 个物品,背包体积为 j 时,所能获得最大价值。
-
写出子问题的递推关系:
//f(i,j)表示只取前 i 个物品,背包体积为 j 时最大价值
当 v[i] > j 时, F(i, j)=F(i-1, j)
当 v[i] ≤ j 时,F(i, j)=max( F(i-1, j), F(i-1,j-v[i]) + p[i])
-
初始条件和边界情况:
当 i = 0 时,即只取前0个物品,也就是没有物品可取,F(i, j) = 0
3.建立模型
设置dp[i][j]数组,记录只取前 i 个物品,背包体积为 j 时,所能获得最大价值。
从取前0个物品到所有物品,背包容量从0到13依次遍历,每次遍历填写dp数组。
从左到右表示背包容量0 → 13 ,从上到下表示取前 0→5个物品。
dp[i][j]:
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 18 18 18 18 18 18 18 18 18 18 18
0 0 0 18 18 18 18 18 20 20 20 38 38 38
0 0 0 18 18 18 18 18 33 33 33 38 38 38
0 0 10 18 18 28 28 28 33 33 43 43 43 48
0 0 10 18 18 28 28 31 33 33 43 43 46 48 <-
遍历结束dp[5][13]即为取5 个物品,背包体积为 13 时,所能获得最大价值。
4.理解题目
c++完整代码:
#include<iostream>
using namespace std;
/**
* 0-1 背包(0-1 knapsack)
* 有5个不同价值不同体积的物品,求在背包容量为13时能获得的最大价值
* 物品价值:[18, 20, 15, 10, 3]
* 物品体积:[ 3, 8, 5, 2, 2]
* tip:背包问题是 动态规划 的典型问题
* 首先我们按顺序决定是否装该物品 则每个物品只有两种情况 装 与 不装
* 所以 若: f(x,y) 表示前x个物品在背包容量为y时的最大价值
* 则: f(5,13) = max(f(5-1,13),f(5-1,13-2)+3)
* f(4,13) = ... f(4,11) = ...
* ...
* 所以自底向上逐层填表即可求解。
*/
const int M = 13; //背包最大容量
const int N = 5; //最大物品数量
int p[] = {0, 18, 20, 15, 10, 3};//五个物品的价值
int v[] = {0, 3, 8, 5, 2, 2}; //五个物品的体积
int dp[N + 1][M + 1]; //dp数组
void dynamic()
{
for (int i = 1; i <= N; i++) //增加物品数量
{
for (int j = v[i]; j <= M; j++) // 增加背包体积
{
if (j < v[i]) // 如果物品体积大于背包容量就不装
{
dp[i][j] = dp[i - 1][j];
}
else
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + p[i]); // 状态转移方程
}
}
}
}
void init() //初始化
{
for(int i=0; i<=N; i++)
for(int j=0; j<=M; j++)
dp[i][j] = 0;
}
int main(){
init(); //初始化
dynamic();
printf(" MAX = %d\n", dp[5][13]);
for(int i=0; i<=N; i++)
{
for(int j=0; j<=M; j++)
{
printf("%5d",dp[i][j]);
}
printf("\n");
}
return 0;
}