1. 程式人生 > >Project Euler 31- Coin sums

Project Euler 31- Coin sums

題目描述

有面值分別為1,2,5,10,20,50,100,200的硬幣,現要湊齊200的面值,請問一共有多少種湊法。

問題分析

不正確的解法

直接想到的方法是使用動規:
狀態轉換方程:f(x) = sum { f(x - a[i]) }
注:a[i]為各種面值

# Coin sums
COINS = [1, 2, 5, 10, 20, 50, 100, 200]
memorandum = {}


def calculate(currency):
    if currency < 0:
        return 0
    elif currency == 0:
        return
1 # if memorandum has, return value elif currency in memorandum: return memorandum[currency] else: global COINS result = 0 for coin in COINS: result += calculate(currency - coin) # set this value to memorandum memorandum[currency] = result if
__name__ == '__main__': for i in xrange(200 + 1): calculate(i) for ele in memorandum: print(str(ele) + ' : ' + str(memorandum[ele]))

但我們仔細想一下就會發現一點問題。如果當前的面值為3,那麼1+2與2+1都會被看作是可行的解,但實際上這兩個是同一種找零。
這樣的答案肯定是不對的。

問題的原因

仔細想一下就會發現,出現這個問題的原因在於我們同時使用了所有的硬幣參與動規而沒有對此加以約束,那麼當前面值下的不同路徑子集樹可能會出現同一種組合。

如何規避這些重複的結果 ?

一種可行的方法是先假設只有一種硬幣,得出一個解,在此之上依次增加硬幣的種類,逐一進行替換。
C#程式碼:

static void Main(string[] args)
        {
            int target = 200;
            int[] coinSizes = { 1, 2, 5, 10, 20, 50, 100, 200 };
            int[] ways = new int[target + 1];
            ways[0] = 1;

            for (int i = 0; i < coinSizes.Length; i++)
            {
                for (int j = coinSizes[i]; j <= target; j++)
                {
                    ways[j] += ways[j - coinSizes[i]];
                }
            }
            Console.WriteLine(ways[200]);
        }