「今週のお題:パターゴルフのコース設計」問題解答 ( CodeIQ )
はじめに
CodeIQという所で週次で出題される「今週のお題」シリーズに解答しましたので、ネタバレです。
問題
今回は次のような問題でした。
- 空白区切りで入力される m,n に対し、mホールで合計n打となる、各ホールの打数の割り振り方が何通りあるかを出力する。ただし、各ホールの打数は1以上5以下とする。
解説
次のRubyのコードを提出しました。
def listup(n,s,&b) return enum_for(__method__,n,s) unless b f=->m,t,u,a{ t==0 ? b.call(a): ((t-1)/m+1..[u,t].min).each{|c|f[m-1,t-c,c,a+[c]]} } f[n,s-n,4,[]] end n,m=gets.split.map(&:to_i) puts listup(n,m).reduce(0){|s,r| t,p,d=n+1 s+r.reduce(1){|q,c|q*(t-=1)/(d=(p==p=c)?d+1:1)} }
再帰的に候補を挙げていくのですが、ホール毎に打数を1~5から選んで並べていたのでは、 並べ方の数が爆発する ( と思った ) ため、一旦打数の組み合わせのみを選び、 それぞれに各ホールへの割り振り方をかけて、合計を求めるようにしています。
例えば、4ホール12打数の場合、[4,4,3,1]という組み合わせがありますが、これに対しては 4C2×2C1 の割り振り方があります。
打数の組み合わせの列挙はlistupにより、並びが降順になるように行います。
※前回の連続する桁の数字で作る平方数に味を占めたのか、またEnumeratorとして扱えるようにしています。
なお、打数1~5を0~4にシフトして考える ( 予め総打数からはホール数を引いておく ) ことで、途中で残り打数が0となった時点で再帰を打ち切るようにしています。つまり、打数の組み合わせからは、打数0の部分は省いています。
終わりに
素直にメモ化再帰を使うのとどっちが速かったんでしょうね…。という点は気になりましたが、まあ通ったので特に比較はしていません。