私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「上下左右に箱を並べよう」(CodeIQ)を参照してください。
大手衣料品店のスマートフォンアプリとコラボレーションして話題になっている、ゲーム機での人気ゲームがあります。
(例えばこのようなゲームがイメージです→) http://www.uniqlo.com/jp/hacoboy/ ※CodeIQ外のサイトに飛びます。
このゲームのように、箱を並べるパターンが何通りあるかを求めることを考えます。
ここでは図のような格子状のマスにおいて、ある位置からスタートして、上下左右の隣り合う位置に箱を配置していきます。
すでに箱が配置されている位置、開始位置には箱を配置できません。
なお、上記のゲームでは最初に下方向に配置することはできませんが、本問では最初から下方向も可能とします。
配置する箱の数 n が与えられたとき、その箱の配置方法が何通りあるかを求めます。
ただし、できあがった位置と形だけで判断するものとし、その手順が異なっても同じ位置・同じ形であれば同じものとします。
例えば n = 3 のとき、以下のパターンはいずれも同じものとします。
n = 3 のとき、その箱の配置方法は以下の図のように上方向からスタートするものが9通り、そのほか全部で32通りがあります。
標準入力から n が与えられたとき、その箱の配置方法が何通りあるかを求め、標準出力に出力してください。
なお、n は 9以下の正の整数とします。
【入出力サンプル】
標準入力
3
標準出力
32
Rubyで解答しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | #!/usr/bin/ruby ROT_90 = [[ 0 , - 1 ], [ 1 , 0 ]] ROT_180 = [[- 1 , 0 ], [ 0 ,- 1 ]] ROT_270 = [[ 0 , 1 ], [- 1 , 0 ]] def rotate(fig, to) ret = [] for f in fig ret << [ to[ 0 ][ 0 ] * f[ 0 ] + to[ 0 ][ 1 ] * f[ 1 ], to[ 1 ][ 0 ] * f[ 0 ] + to[ 1 ][ 1 ] * f[ 1 ] ] end return ret.sort_by{|x| [x[ 0 ], x[ 1 ]]} end def rotateAll(arr, to) ret = [] for a in arr ret << rotate(a, to) end return ret end def make(n, st) dir = [[ 0 , 1 ], [ 1 , 0 ], [ 0 ,- 1 ], [- 1 , 0 ]] # 上右下左 queue = [st] for _ in 0 ...n nq = [] while !queue.empty? now = queue.shift for d in dir nxt = [now.last[ 0 ]+d[ 0 ], now.last[ 1 ]+d[ 1 ]] next if now.include?(nxt) nq << now + [nxt] end end queue = nq end ret = [] for q in queue ret << q.sort_by{|x| [x[ 0 ], x[ 1 ]]} end return ret end def solve(n) up = make(n- 1 , [[ 0 , 0 ], [ 0 , 1 ]]) left = rotateAll(up, ROT_90 ) down = rotateAll(up, ROT_180 ) right = rotateAll(up, ROT_270 ) ret = up + left + down + right ret = ret.uniq return ret.size end # main while line = gets line.strip! p solve(line.to_i) end |
1 | 2 | 3 | 2 | 2 | 1 | |||||||||
0 | 3 | 0 | 1 | 3 | 0 | |||||||||
入力値を数値にしてsolve()に渡し、結果を印字します。
まず、最初の箱を上方向におくパターンだけで図形を列挙します。
それを元に90度、180度、270度回転した図形の座標リストを作成します。
これらの結果得られる座標は全てソート済みです。
この座標リストを連結してArray#uniq()で重複を削除し、リストのサイズを返します。
nは手順回数(入力値-1)、stは1手順目を配置した座標リストで実際には[[0,0], [0,1]]が渡ってきます。
dirは箱を置く方向のリストです。
queueには途中状態の図形のリストが入ります。初期状態は引数stだけが入っています。
手順回数(入力値-1)だけループし、queueから最初の要素を取り出して、dirの各方向に箱を配置します。
この時、すでに箱が置かれている場所に再度置こうとした場合は不正な操作なので無視します。
箱が置けた場合、次の候補(変数nq)にその座標リストを追加します。
queueが空になったらnqでqueueを置換します。
n(入力値-1)回の操作を終えたら作成可能な図形が列挙されているので、x座標(優先)、y座標(2番目のソート条件)でソートします。これで同じ図形は同じ座標が同じ順番で並んでいます。
ソート済みの座標リストを返却します。
図形の回転を計算します。高校で習うベクトルの回転です。
figは図形1つ分の座標リストで、toは回転のための行列です。toには定数ROT_A、ROT_B、ROT_Cのいずれかを受け取ります。
回転後の座標リスト(ソート済み)を返却します。
arrは図形の座標リストの配列で、その全てに対してtoに指定した行列(定数ROT_A、ROT_B、ROT_Cのいずれか)で回転後の図形の座標リストの配列を返します。
単純に総当たりのコードです。多分、パターン数だけを計算することもできると思いますが私には数学が足りません。
この問題が★★ではなく★★★なのは座標の回転の知識が必要な分★1個増えたのでしょうか?