from itertools import groupby
from collections import namedtuple

def anyvalidcomb(items, maxwt, val=0, wt=0):
    ' All combinations below the maxwt '
    if not items:
        yield [], val, wt
    else:
        this, *items = items            
        for n in range(this.number + 1):
            w = wt  + n * this.weight
            if w > maxwt:
                break
            v = val + n * this.value
            this_comb = [this] * n
            for comb, value, weight in anyvalidcomb(items, maxwt, v, w):
                yield this_comb + comb, value, weight

maxwt = 400
COMB, VAL, WT = range(3)
Item  = namedtuple('Items', 'name weight value number')
items = [ Item(*x) for x in
          (
            (, 9, 150, 1),
            (, 13, 35, 1),
            (, 153, 200, 3),
            (, 50, 60, 2),
            (, 15, 60, 2),
            (, 68, 45, 3),
            (, 27, 60, 3),
            (, 39, 40, 3),
            (, 23, 30, 1),
            (, 52, 10, 3),
            (, 11, 70, 1),
            (, 32, 30, 1),
            (, 24, 15, 2),
            (, 48, 10, 2),
            (, 73, 40, 1),
            (, 42, 70, 1),
            (, 43, 75, 1),
            (, 22, 80, 1),
            (, 7, 20, 1),
            (, 18, 12, 2),
            (, 4, 50, 1),
            (, 30, 10, 2),
           ) ]  

bagged = max( anyvalidcomb(items, maxwt), key=lambda c: (c[VAL], -c[WT])) 
print(% len(bagged[COMB]))
print('\n\t'.join('%i off:%s'% (len(list(grp)), item.name) for item, grp in groupby(sorted(bagged[COMB]))))
print(% bagged[1:])