Вот таблица реализации и небольшая проработка красивого ответа algrid . Это дает ответ на f(500, [1, 2, 6, 12, 24, 48, 60])
примерно за 2 секунды.
Простое объявление C(n, k, S) = sum(C(n - s_i, k - 1, S[i:]))
означает добавление всех способов получить текущую сумму, n
с использованием k
монет. Затем, если мы разделим n
на все способы, которыми он может быть разбит на две части, мы можем просто добавить все способы, которыми каждая из этих частей может быть изготовлена из того же числа, k
, из монет.
Красота фиксирования подмножества монет, которое мы выбираем, в уменьшающемся списке означает, что любая произвольная комбинация монет будет учитываться только один раз - она будет учитываться при расчете, где крайняя левая монета в комбинации является первой монетой в наше уменьшающееся подмножество (при условии, что мы упорядочиваем их таким же образом). Например, произвольное подмножество [6, 24, 48]
, взятое из [1, 2, 6, 12, 24, 48, 60]
, будет учитываться только при суммировании для подмножества [6, 12, 24, 48, 60]
, так как следующее подмножество [12, 24, 48, 60]
не будет включать 6
и предыдущее подмножество [2, 6, 12, 24, 48, 60]
имеет хотя бы одну 2
монету.
Код Python (см. здесь ; подтвердите здесь ):
import time
def f(n, coins):
t0 = time.time()
min_coins = min(coins)
m = [[[0] * len(coins) for k in xrange(n / min_coins + 1)] for _n in xrange(n + 1)]
# Initialize base case
for i in xrange(len(coins)):
m[0][0][i] = 1
for i in xrange(len(coins)):
for _i in xrange(i + 1):
for _n in xrange(coins[_i], n + 1):
for k in xrange(1, _n / min_coins + 1):
m[_n][k][i] += m[_n - coins[_i]][k - 1][_i]
result = 0
for a in xrange(1, n + 1):
b = n - a
for k in xrange(1, n / min_coins + 1):
result = result + m[a][k][len(coins) - 1] * m[b][k][len(coins) - 1]
total_time = time.time() - t0
return (result, total_time)
print f(500, [1, 2, 6, 12, 24, 48, 60])