私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「ペア・ドロップ」(CodeIQ)を参照してください。
n を自然数とします。1 から n までの自然数が 1 つずつ書かれた n 枚のカードが 2 組あります。
これら 2n 枚のカードをよく混ぜ、A と B の 2 人に n 枚ずつ配ります。
A と B は、それぞれ自分の持ち札の中に番号が一致するカードがあればその 2 枚を捨てます。
捨てられるカードを全て捨てた後の A の持っているカードの枚数が k である確率を F(n, k) とします。
例えば F(1, 0)=0, F(1, 1)=1, F(2, 0)=1/3, F(2, 1)=0, F(2, 2)=2/3, F(4, 2)=24/35 です。
標準入力から、自然数 n と k (1 ≦ k ≦ n ≦ 100)が半角空白区切りで与えられます。
標準出力に 106×F(n, k) の整数部分の値を出力するプログラムを書いてください。
例えば、(n, k)=(2, 2) であれば 666666 と出力してください。
(n, k)=(4, 2) であれば 685714 と出力してください。
なお、全てのテストケースで、 106×F(n, k) の値とその最寄りの整数は 10-3 以上離れていることが保証されています。
Rubyで解答しています。
#!/usr/bin/ruby def combin(n,k) ret = 1 n.downto(n-k+1){|i| ret *= i } k.downto(2){|i| ret /= i } return ret end def a051288(n, k) return (combin(n, 2*k) * 2**(n-2*k) * combin(2*k, k)).to_i end def a000984(n) return combin(2*n, n) end def solve(n, k) l = n - k return 0 if (l%2) == 1 ret = Rational(a051288(n, l/2), a000984(n)) return (ret * 1000000).to_i end # main while line = gets line.strip! next if line.empty? n, k = line.split.map{|a| a.to_i} p solve(n, k) end
★★★の問題です。
この出題者の問題の場合よくあることですが、私には数学が足りないので自力では無理でOEIS行きでした。
ペア数 | ||||
0 | 1 | 2 | ||
n | 1 | 2 | - | - |
2 | 4 | 2 | - | |
3 | 8 | 12 | - | |
4 | 16 | 48 | 6 |
入力値を数値にしてsolve()に渡し、結果を印字します。
n個の中からk個を選んだ場合の組み合わせ数を求めます。
nCkの公式通りです。
A051288(n,k)を実装しただけです。
A000984(n)を実装しただけです。
まず、入力値kは手札の残りなので、捨てたカードの枚数lを求めます。
lが奇数というパターンは無いのでlが奇数なら0を返します。
lの1/2がペアの数なのでa051288()の第二引数にはl/2を渡します。これでa051288()を使って捨てたペアの組み合わせ数を求めることができます。
後は分母をa000984()で計算して、結果を計算すればOKです。
私はRationalを使用していますが、これはデバッグのためです。retを印字すれば結果が分数で確認できるので問題の例を確認するためにRationalを利用しただけで、別に不要です。
最初、答えが分数(1000000倍する前)だしOEISでは探せないからやめようかと思っていましたが、分母と分子に分けて考えると絶対にOEISで見つかるな、ということで探したら予想通り見つかりました。
A000986の数列はともかく、A051288を自分で考えられる人はすごいと思います。