views:

119

answers:

3

Consider the problem in which you have a value of N and you need to calculate how many ways you can sum up to N dollars using [1,2,5,10,20,50,100] Dollar bills.

Consider the classic DP solution:

C = [1,2,5,10,20,50,100]

def comb(p):
    if p==0:
        return 1
    c = 0
    for x in C:
        if x <= p:
            c += comb(p-x)
    return c 

It does not take into effect the order of the summed parts. For example, comb(4) will yield 5 results: [1,1,1,1],[2,1,1],[1,2,1],[1,1,2],[2,2] whereas there are actually 3 results ([2,1,1],[1,2,1],[1,1,2] are all the same).

What is the DP idiom for calculating this problem? (non-elegant solutions such as generating all possible solutions and removing duplicates are not welcome)

+5  A: 

Not sure about any DP idioms, but you could try using Generating Functions.

What we need to find is the coefficient of x^N in

(1 + x + x^2 + ...)(1+x^5 + x^10 + ...)(1+x^10 + x^20 + ...)...(1+x^100 + x^200 + ...)

(number of times 1 appears*1 + number of times 5 appears * 5 + ... )

Which is same as the reciprocal of

(1-x)(1-x^5)(1-x^10)(1-x^20)(1-x^50)(1-x^100).

You can now factorize each in terms of products of roots of unity, split the reciprocal in terms of Partial Fractions (which is a one time step) and find the coefficient of x^N in each (which will be of the form Polynomial/(x-w)) and add them up.

You could do some DP in calculating the roots of unity.

Moron
Damn, I love it. I like this attitude a little bit less, when it's me who asks a question, but it's not the case now, so +1 :).
Maciej Hehl
@Mac: Sorry, I didn't mean to offend anyone. Since the question is asking for elegant solutions, I thought this fits the bill.
Moron
+3  A: 

You should not go from begining each time, but at max from were you came from at each depth. That mean that you have to pass two parameters, start and remaining total.

C = [1,5,10,20,50,100]

def comb(p,start=0):
    if p==0:
        return 1
    c = 0
    for i,x in enumerate(C[start:]):
        if x <= p:
            c += comb(p-x,i+start)
    return c 

or equivalent (it might be more readable)

C = [1,5,10,20,50,100]

def comb(p,start=0):
    if p==0:
        return 1
    c = 0
    for i in range(start,len(C)):
        x=C[i]
        if x <= p:
            c += comb(p-x,i)
    return c 
ralu
it could be made a bit more readable, but the idea is correct.
Nikita Rybak
+1  A: 

Terminology: What you are looking for is the "integer partitions" into prescibed parts (you should replace "combinations" in the title). Ignoring the "dynamic programming" part of the question, a routine for your problem is given in the first section of chapter 16 ("Integer partitions", p.339ff) of the fxtbook, online at http://www.jjj.de/fxt/#fxtbook