[動態規劃] 三、最少硬幣組成某面值

ajiang02發表於2020-02-16

題目

如果我們有面值為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 協議》,轉載必須註明作者和本文連結

相關文章