私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「指定された回数で移動できる経路は何通り?」(CodeIQ)を参照してください。
横に m マス、縦に n マス並んだ格子状のマスがあり、左上の隅から右下の隅までマスの周囲または対角線に沿って移動することを考えます。
ただし、対角線は左上から右下への線のみ可能とします。
(縦でも横でも斜めでも、いずれも1回で1マス分移動します)
移動は「右」「下」「右下」のいずれかとし、左や上、左上などに移動することはできません。
標準入力から m, n と合わせて移動回数 a がスペース区切りで与えられるとき、ちょうど a 回の移動で右下の隅に到達する経路の数が何通りあるかを求め、標準出力に出力してください。
ただし、m, n は20以下の正の整数、a は m + n 以下の正の整数とします。
例えば、m = 3, n = 2, a = 3のとき、以下のような3通りが考えられます。
【入出力サンプル】
標準入力
3 2 3
標準出力
3
Rubyで解答しています。
#!/usr/bin/ruby $Memo = {} def solve(m, n, a, pos) return 0 if (pos[0] > m) || (pos[1] > n) return $Memo[[pos[0], pos[1], a]] if $Memo[[pos[0], pos[1], a]] != nil if a == 0 then if (pos[0] == m) && (pos[1] == n) then return 1 else return 0 end end dir = [[1,0], [0,1], [1,1]] ret = 0 for d in dir ret += solve(m, n, a-1, [pos[0]+d[0], pos[1]+d[1]]) end $Memo[[pos[0], pos[1], a]] = ret return ret end # main while line = gets line.strip! next if line.empty? m,n,a = line.split.map{|v| v.to_i} p solve(m, n, a, [0,0]) end
よくあるメモ化再帰の問題です。
入力値を数値にしてsolve()に渡し、結果を印字します。
引数m,n,aは入力値です。
posは現在の座標(行,列)です。
6〜12行目は終了条件のチェックです。
座標が範囲外の時はダメなので0を返します(6行目)。
メモに結果がある場合は再計算せず値を返却します(7行目)。
a(残り移動回数)が0の場合(8行目)、座標が右下なら1を返却し(9行目)、それ以外なら0を返却します(10行目)。
dirは下、右、右下への移動を示す値です。
retは引数の座標、残り移動回数から右下に到達する経路のパターン数の合計です。
16〜18行目のループで移動し、その座標と残り移動回数を1減らした値でsolve()を再帰呼び出しし、結果をretに加算します。
20行目でメモに当該引数の場合のパターン数を記録します。
retを返却します。
よくあるタイプの問題だったので簡単に終わりました。
もし、これが上、左、左上への移動が認められているならかなり苦労したと思います。