Dynamic programming is all about solving task by compounding solutions of smaller sub-tasks. Most
famous example is a definition of Fibonacci number `f(n) = f(n-1) + f(n-2)`

.

For the coin task recursive solution is:

def score(solution): return sum(solution.values()) def solve(amount, coins): if amount in coins: return {amount: 1} best_solution = None best_score = None for coin in coins: if amount < coin: continue solution = solve(amount - coin, coins) if not solution: continue curr_score = score(solution)+1 if not best_score or curr_score < best_score: if coin in solution: solution[coin] += 1 else: solution[coin] = 1 best_solution = solution best_score = curr_score return best_solution

Memoization should be used to reduce the size of recursion tree.

Alternatively instead of recursion we may start from the smallest sub-problems and iteratively build solution for larger and larger problems until we reached a desired solution.

Unfortunately we may calculate unnecessary sub-problems with iterative approach, this is not happened with recursion and memoization approach.

For:

print solve(10, set([1, 2, 3])) print solve(10, set([2, 3])) print solve(11, set([2, 5])) print solve(32, set([7, 10]))

I've got:

{1: 1, 3: 3} {2: 2, 3: 2} {2: 3, 5: 1} None