動態規劃屬於「自底向上」填表,而回溯法則是「自頂向下」搜索狀態空間樹。配合分支限界剪枝,可大幅減少無效搜索,適合物品數量較大但承重較小的場景。
n,葉子節點代表完整方案。W。將剩餘物品按單位價值 v/w 降序排列,允許「分數裝包」計算上界:
function bound($cp, $cw, $k, $W) {
global $v_arr, $w_arr, $n;
$b = $cp; $c = $cw;
for ($i = $k + 1; $i < $n; $i++) {
$c += $w_arr[$i];
if ($c < $W) {
$b += $v_arr[$i];
} else {
$b += (1 - ($c - $W) / $w_arr[$i]) * $v_arr[$i];
break;
}
}
return $b;
}
<?php
$v_arr = [11, 21, 31, 33, 43, 53, 55, 65];
$w_arr = [1, 11, 21, 23, 33, 43, 45, 55];
$n = count($w_arr);
$W_total = 110;
$bestV = -1; $bestX = [];
$Y = array_fill(0, $n, 0);
$cw = $cp = 0; $k = 0;
while (true) {
// 向左走:盡量裝入
while ($k < $n && $cw + $w_arr[$k] <= $W_total) {
$cw += $w_arr[$k];
$cp += $v_arr[$k];
$Y[$k] = 1;
$k++;
}
// 到達葉子或無法再裝入
if ($k == $n) {
if ($cp > $bestV) { $bestV = $cp; $bestX = $Y; }
$k = $n - 1;
} else {
$Y[$k] = 0; // 嘗試不裝入
}
// 回溯 + 剪枝
while ($k >= 0 && $Y[$k] != 1) $k--;
if ($k < 0) break; // 搜索結束
// 撤銷第 k 件
$Y[$k] = 0;
$cw -= $w_arr[$k];
$cp -= $v_arr[$k];
// 剪枝判斷
while ($k >= 0 && bound($cp, $cw, $k, $W_total) <= $bestV) {
while ($k >= 0 && $Y[$k] != 1) $k--;
if ($k < 0) break 2;
$Y[$k] = 0;
$cw -= $w_arr[$k];
$cp -= $v_arr[$k];
}
$k++;
}
echo "最優價值: $bestV\n方案: " . implode(',', $bestX);
| 維度 | 動態規劃 | 回溯+分支限界 |
|---|---|---|
| 時間複雜度 | O(nW) 確定 | 最好 O(2^n),最壞 O(2^n),平均遠優於窮舉 |
| 空間複雜度 | O(nW) 或 O(W) | O(n) 遞歸棧/陣列 |
| 適用場景 | W 較小、需精確解 | n 較大、W 較大、需單一最優解 |
實際項目中可根據數據規模靈活選擇,甚至混合使用(如先用貪心得初始解作為下界)。