題目
如果我們有面值為1元、3元和5元的硬幣若干枚,如何用最少的硬幣湊夠11元?
總額:11元
硬幣:1元、3元、5元
動態規劃問題建模階段
設硬幣面值為 value[1,3,5],總額為 sum,硬幣個數為 coin
硬幣面值 | 1 3 5
——————————+—————————————
湊夠 0元 | 0 0 0
湊夠 1元 | 1 0 0
湊夠 2元 | 2 0 0
湊夠 3元 | 3 1 0
湊夠 4元 | 4 2 0
湊夠 5元 | 5 3 1
湊夠 6元 | 6 2 2
湊夠 7元 | 7 3 3
湊夠 8元 | 8 2 2
湊夠 9元 | 9 3 3
湊夠 10元 | 10 4 2
湊夠 11元 | 11 3 3
規律:
0元 = 0
1元 = (1-1)元硬幣數 + 1 = 0+1 = 1
2元 = (2-1)元硬幣數 + 1 = 1+1 = 2
3元 = min( (3-1)元硬幣數+1, (3-3)元硬幣數+1 ) = min(2+1, 0+1) = 1
……
11元 = min( (11-1)元硬幣數+1, (11-3)元硬幣數+1,(11-5)元硬幣數+1 ) = min( 10+1, 2+1, 2+1) = 3
由此可見,只需要儲存前面的最小硬幣數,就可推出後面的最小硬幣數
動態規劃三個重要的概念
最優子結構:
由於有三種面值1,3,5,所以湊夠11元有三種方案(選最小值)
11元硬幣數 = min( (11-1)元硬幣數+1, (11-3)元硬幣數+1, (11-5)元硬幣數+1 )
邊界:
當 sum = 0, coin = 0
轉態轉移公式:
d(sum) = min{ d(sum-value[j])+1 } ,這裡的value[j]是硬幣面值[1,3,5]
(例:d(11) = min( d(11-1)+1, d(11-3)+1), d(11-5)+1 )
動態規劃求解問題階段
class Index
{
/**
* 動態規劃(Dynamic Programming)
* 時間複雜度:O(sum * value)
* 空間複雜度:O(sum)
*/
public function test()
{
$sum = 11; // 總額
$value = array(1,3,5); // 硬幣面值
$coin = array(); // 最少硬幣數量
$v_len = count($value); // 每迭代一層都需要迴圈3次硬幣面值
// 給存放最少硬幣數量的陣列賦初始值,不能全初始化為 0,否則在下個 for 內層迴圈的 if 出錯
for ($i = 0; $i<=$sum;$i++) {
$coin[$i] = $i;
}
// 迴圈總額,即迭代表格每一層
for ($i = 0; $i <= $sum; $i++) {
// 迴圈硬幣面值 1 3 5
for ($j = 0; $j < $v_len; $j++) {
// 如果當前金額>=硬幣面值 && 最少硬幣[當前金額-當前硬幣面值]+1<最少硬幣[當前金額]
if ($i >= $value[$j] && $coin[$i-$value[$j]]+1 < $coin[$i]) {
// d(sum) = min{ d(sum-value[j])+1 }
$coin[$i] = $coin[$i-$value[$j]]+1;
}
}
}
halt($coin);
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結