/2009/b-round-1A/C-Collecting-Cards.py
https://bitbucket.org/rndblnch/google-code-jam · Python · 139 lines · 101 code · 22 blank · 16 comment · 8 complexity · fc35fc73de28c4f03d8a0933fdc9518f MD5 · raw file
- #!/usr/bin/env python
- # -*- coding: utf8 -*-
- __author__ = "renaud blanch <rndblnch at gmail dot com>"
- __copyright__ = "Copyright © 2010 - Renaud Blanch"
- __licence__ = "GPLv3 [http://www.gnu.org/licenses/gpl.html]"
- import sys
- next_line = sys.stdin.next
- """
- Collecting Cards
- [http://code.google.com/codejam/contest/dashboard?c=188266#s=p2]
-
- Problem
- You've become addicted to the latest craze in collectible card games,
- PokeCraft: The Gathering.
- You've mastered the rules! You've crafted balanced, offensive, and defensive
- decks! You argue the merits of various cards on Internet forums!
- You compete in tournaments! And now, as they just announced their huge new set
- of cards coming in the year 2010, you've decided you'd like to collect every
- last one of them! Fortunately, the one remaining sane part of your brain is
- wondering: how much will this cost?
- There are C kinds of card in the coming set.
- The cards are going to be sold in "booster packs", each of which contains N
- cards of different kinds.
- There are many possible combinations for a booster pack where no card is
- repeated.
- When you pay for one pack, you will get any of the possible combinations with
- equal probability.
- You buy packs one by one, until you own all the C kinds.
- What is the expected (average) number of booster packs you will need to buy?
- Input
- The first line of input gives the number of cases, T.
- T test cases follow, each consisting of a line containing C and N.
- Output
- For each test case, output one line in the form
- Case #x: E
- where x is the case number,starting from 1, and E is the expected number of
- booster packs you will need to buy.
- Any answer with a relative or absolute error at most 10-5 will be accepted.
- Limits
- 1 ? T ? 100
- Small dataset
- 1 ? N ? C ? 10
- Large dataset
- 1 ? N ? C ? 40
- Sample
- Input
-
- 2
- 2 1
- 3 2
- Output
- Case #1: 3.0000000
- Case #2: 2.5000000
- """
- from collections import defaultdict
- _combinations = {}
- def combination(n, k):
- """compute C_n^k using pascal triangle."""
- key = n, k
- try:
- return _combinations[key]
- except KeyError:
- pass
- if n < k or k < 0:
- cnk = 0
- elif k in [0, n]:
- cnk = 1.
- else:
- cnk = combination(n-1, k-1) + combination(n-1, k)
- _combinations[key] = cnk
- return cnk
- def average(C, N, error=1e-5):
- # build p(i, j) the probability to end with j cards after a step
- # when already having i cards
- p = defaultdict(lambda: 0.)
- nsets = combination(C, N)
- for i in xrange(N, C+1): # i cards already
- for j in xrange(i, C+1): # a set latter, probability to have j cards
- p[i, j] = combination(C-i, j-i) * combination(i, N-j+i) / nsets
-
- # P[j] the probability of having j cards at step t
- P = [0. if j != N else 1. for j in xrange(C+1)]
- t = 1
-
- # average number of steps
- a = 0
-
- P_C = P[C]
- da = t * P_C
-
- # step while absolute and relative errors are limited
- while P_C <= 0 or da > error or da > a*error:
- a += da
-
- # step
- for j in reversed(xrange(N, C+1)):
- P[j] = sum(P[i] * p[i,j] for i in xrange(N, j+1))
-
- t += 1
- da = t * (P[C] - P_C)
- P_C = P[C]
-
- return a
- T = int(next_line())
- for X in xrange(T):
- print "Case #%s:" % (X+1),
- C, N = [int(w) for w in next_line().split()]
- print "%0.5f" % average(C, N)