现有题:有A,B,C,D这四物品,其重量分别为3,4,1,5。其价值分别是10,40,30,20。有一个能装重量为6的背包,问怎么装物品能使背包在不超过其承重范围而获得最大价值。
解决这个问题最快捷的方式是运用动态规划法。什么是动态规划:把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解。
求这道题可以将重量6分隔成为6种重量。分别去求装满0,装满1…等各种情况。最终就可以的出装到重量6时的最佳装法。
可以列出表格进行分析:
~ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
A(3,10) | |||||||
B(4,40) | |||||||
C(1,30) | |||||||
D(5,20) |
从第一行开始,第一列为用A装满重量为0的背包,此时这种情况是,你只能不装东西,其价值就为0。依次往后走,直到到了重量为3的背包,此时你正好可以将A放入背包中,其价值为10,依次往最后到重量6,背包都能装下物品A,且此时的背包价值为10。
~ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
A(3,10) | 0 | 0 | 0 | 10 | 10 | 10 | 10 |
B(4,40) | |||||||
C(1,30) | |||||||
D(5,20) |
从第二行开始,用如上的方法将B装入背包中。此时要注意的是当背包重量为3时,是可以装下A,但是装不下B,所以此时的背包的最大价值是10。从重量4开始,可以装下B,此时B的价值是40,比装下物品A的价值还要大所以只装物品B,依次继续。
~ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
A(3,10) | 0 | 0 | 0 | 10 | 10 | 10 | 10 |
B(4,40) | 0 | 0 | 0 | 10 | 40 | 40 | 40 |
C(1,30) | |||||||
D(5,20) |
从第三行开始,物品C是在背包重量为1的时候开始可以被装下的,此时值为30。
~ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
A(3,10) | 0 | 0 | 0 | 10 | 10 | 10 | 10 |
B(4,40) | 0 | 0 | 0 | 10 | 40 | 40 | 40 |
C(1,30) | 0 | 30 | 30 | 30 | 40 | 70 | 70 |
D(5,20) |
按照上述方法最终可得到表格
~ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
A(3,10) | 0 | 0 | 0 | 10 | 10 | 10 | 10 |
B(4,40) | 0 | 0 | 0 | 10 | 40 | 40 | 40 |
C(1,30) | 0 | 30 | 30 | 30 | 40 | 70 | 70 |
D(5,20) | 0 | 30 | 30 | 30 | 40 | 70 | 70 |
所以最大的价值是70。
代码如下:
$weight = 5; //背包重量
$total = 4; //物品种类
$arr = [['A', 3, 10], ['B', 4, 40], ['C', 1, 30], ['D', 5, 20]]; //物品数组
//构造不同重量的背包的价值数组
$values = array_fill(0, $weight + 1, 0);
//构造重量所属物品数组
$items = array_fill(0, $weight + 1, 0);
for ($i = 0; $i < $total; $i++) {
for ($j = $arr[$i][1]; $j <= $weight; $j++) {
//各个阶段的袋子存入物品后剩余重量
$left_weight = $j - $arr[$i][1];
//出入物品后新的价值
$new_value = $arr[$i][2] + $values[$left_weight];
//如果当前的最新价值大于历史最大价值时,替换values数组所属重量的值
if($new_value > $values[$j]) {
$values[$j] = $new_value;
//记录下次物品
$items[$j] = $i;
}
}
}
echo "最大价值是 {$values[$weight]}<br/>\n";
echo "物品名称 重量 价值<br/>\n";
//找出最大价值所属各个物品,从下往上(重量)递推
for ($j = $weight; 1 <= $j; $j = $j - array[$items[$j]][1]) {
echo array[$items[$j]][0] . " " . array[$items[$j]][1] . " " . array[$items[$j]][2] . "<br/>\n";
}