在使用动态规划思想解决的问题中,最常见的是背包的问题,而在背包问题中,最简单的就是0-1背包。
0-1背包的问题形式如下:有一背包,只能装重量为V的物品,有n个的物品,这些物体体积为w,价值为v,在每个物品最多装一个的情况下,怎么装物品可以让背包中的物品价值最大。该问题之所以可以用动态规划来解决,是由于将“大问题”拆解后的“小问题”存在着依赖关系,并不独立,这也是动态规划和分治最大的区别。
在对该问题进行分析之前,可以先定义一些变量,来辅助分析。具体为:value[ i ]表示第i个物品的价值,weight[ i ]表示第i个物品的体积,dp[ i ][ j ]表示当前背包容量为j时,前i个物品最佳组合对应的价值。为方便分析,设定物品个数为4,背包重量限制为8,示例数据如下:
体积(weight) | 2 | 3 | 4 | 5 |
价值(value) | 3 | 4 | 5 | 6 |
分析步骤如下:
1、构建一张动态规划表dp,表的大小为(物品个数+1)*(背包重量限制+1)。dp[0][0]-dp[0][物品+1]和dp[0][0]-dp[0][背包重量限制+1]全为0,如下:
个数/重量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | ||||||||
2 | 0 | ||||||||
3 | 0 | ||||||||
4 | 0 |
2、以dp[ i ][ j ],即背包可装重量为j,第i个物品的状态来进行分析。此时可能进行的动作有如下几种:
1>要装入物品的重量比背包可装的重量要大,此时不能再装入新物品,即dp[ i ][ j ] = dp[i - 1][ j ];
2>此时能装下新物品,但具体要不要装入则需要看是否能达到最大价值,如果达不到,则和不能装入效果一样,即 dp[ i ][ j ] = dp[i - 1][ j ]。如果能装入,则dp[i - 1][j - weight[ i ]] + value[ i ]。
寻找最大价值的过程,就是一个逐行不断填表的过程,最终填表结果如下:
物品/重量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
2 | 0 | 0 | 3 | 4 | 4 | 7 | 7 | 7 | 7 |
3 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 9 |
4 | 0 | 0 | 3 | 4 | 5 | 7 | 8 | 9 | 10 |
Java代码如下:
int[ ] weight = {0,2,3,4,5}; //商品的重量2、3、4、5
int[ ] value = {0,3,4,5,6}; //商品的价值3、4、5、6
int bagCap = 8; //背包承受的重量限制
int[ ][ ] dp = new int[weight.length][bagCap+1]; //动态规划表
//构建动态规划表
for (int i = 1; i <= weight.length-1; i++) {
for (int j = 1; j <= bagCap; j++) {
if (j < weight[ i ])
dp[ i ][ j ] = dp[i - 1][ j ];
else
dp[ i ][ j ] = Math.max(dp[i - 1][ j ], dp[i - 1][j - weight[ i ]] + value[ i ]);
}
}
//动态规划表的输出
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 9; j++) {
System.out.print(dp[ i ][ j ]+" ");
}
System.out.print("\n");
}