私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「世界は歪んでいる。それでも君は歩む。」(CodeIQ)を参照してください。
コマンド | 意味 |
---|---|
L | 反時計回りにおよそ90度向きを変える |
R | 時計回りにおよそ90度向きを変える |
1~9 | 1〜9 マス、向いている方向にマス目にそって進む |
入力 | 出力 |
---|---|
5R1R2L1 | 0369cfgdab |
3R2 | 0369ab |
R2L6LL55R5L5 | 01258behkheb852! |
Rubyで解答しています。
#!/usr/bin/ruby # 地図 # 二次元配列で表す # つながっている部分は'='で示す MAP = [ # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ['!', '!', '!', '!', '!', '!', '!', '!', '!', '!', '!', '!'], # 0 ['!', '0', '3', '6', '9', 'c', 'f', 'i', 'n', 'm', 'l', '!'], # 1 ['!', '1', '4', '7', 'a', 'd', 'g', 'j', 'q', 'p', 'o', '!'], # 2 ['!', '2', '5', '8', 'b', 'e', 'h', 'k', 't', 's', 'r', '!'], # 3 ['!', 'Y', 'Z', '@', '!', '!', '!', '!', '=', '=', '=', '!'], # 4 ['!', 'V', 'W', 'X', '!', '!', '!', '!', '!', '!', '!', '!'], # 5 ['!', 'S', 'T', 'U', '!', '!', '!', '!', '!', '!', '!', '!'], # 6 ['!', 'P', 'Q', 'R', '!', '!', '!', '!', '!', '!', '!', '!'], # 7 ['!', 'M', 'N', 'O', 'F', 'C', 'z', 'w', '=', '!', '!', '!'], # 8 ['!', 'J', 'K', 'L', 'E', 'B', 'y', 'v', '=', '!', '!', '!'], # 9 ['!', 'G', 'H', 'I', 'D', 'A', 'x', 'u', '=', '!', '!', '!'], # 10 ['!', '!', '!', '!', '!', '!', '!', '!', '!', '!', '!', '!'], # 11 ] class Position def initialize() @x = 1 @y = 1 @dx = 1 @dy = 0 @path = "0" end # 方向を変える def _changeDirection(nxt) if nxt == 'R' then if (@dx == 1) && (@dy == 0) then @dx = 0; @dy = 1 elsif (@dx == 0) && (@dy == 1) then @dx = -1; @dy = 0 elsif (@dx == -1) && (@dy == 0) then @dx = 0; @dy = -1 elsif (@dx == 0) && (@dy == -1) then @dx = 1; @dy = 0 end elsif nxt == 'L' then if (@dx == 1) && (@dy == 0) then @dx = 0; @dy = -1 elsif (@dx == 0) && (@dy == 1) then @dx = 1; @dy = 0 elsif (@dx == -1) && (@dy == 0) then @dx = 0; @dy = 1 elsif (@dx == 0) && (@dy == -1) then @dx = -1; @dy = 0 end else end end # 1歩進む # 進んだ位置の文字を返す # t,s,r、w,v,uからの移動は特殊なパターンがあるのでそれを処理する # t,s,rからyの増加方向に進んだ場合、w,v,uに接続する # w,v,uからxの増加方向に進んだ場合、t,s,rに接続する # この際、方向も90度変更する def _toNext() nx = @x + @dx ny = @y + @dy pos = MAP[ny][nx] if pos == '=' then case MAP[@y][@x] when 't' nx = 7; ny = 8; @dx = -1; @dy = 0 when 's' nx = 7; ny = 9; @dx = -1; @dy = 0 when 'r' nx = 7; ny = 10; @dx = -1; @dy = 0 when 'w' nx = 8; ny = 3; @dx = 0; @dy = -1 when 'v' nx = 9; ny = 3; @dx = 0; @dy = -1 when 'u' nx = 10; ny = 3; @dx = 0; @dy = -1 end end @x = nx; @y = ny return MAP[@y][@x] end # 指定された数字だけ進む # 奈落に落ちたらfalse、そうでなければtrueを返す def _forward(nxt) for _ in 0...nxt.to_i ret = _toNext() @path += ret if ret == '!' then return false end end return true end # 入力値に従って移動する # 通ったパスの文字列を返す # 奈落に落ちたら終わる # input R,L,1~9 def move(input) for nxt in input.chars if (nxt == 'R') || (nxt == 'L') then _changeDirection(nxt) else ret = _forward(nxt) if ret == false then break end end end return @path end attr_accessor :x attr_accessor :y end while line = gets line.strip! if line.empty? then next end pos = Position.new ret = pos.move(line) puts ret end
問題の難易度は★★★ですが、アルゴリズム的に難しいところはありません。
やればできると言うタイプの問題です。
一番の問題は地図をどうするかということです。
組み込みのデータ型でうまく表現する方法は(多分どんな言語でも)ないでしょう。
私は二次元配列にしておいて別の場所につながるところまできたらそれを例外的に処理するという方法にしました。この方法は単純で解りやすいです。
地図の定義です。先に書いた通り2次元配列で外周1マス分余分に確保しています。
それぞれの座標の値はマップ上の文字(領域外は'!')ですが、三角形の右下部分は'='を設定し、この文字の場所に来たら対応する場所に座標を書き換え、方向を調整することで三角形のマップに一致させます。
移動と位置情報を管理するクラスです。
次のメンバを持ちます。
[変数]
x, y: 現在の座標です。
dx, dy: 現在x,yのどちらを向いているかで、それぞれ±1(例外的に初期値として0)をとります。
path: 経路情報です。回答はこれを印字します。
[関数]
move(): 入力をパースして移動を処理します。
_changeDirection(): 入力がRかLの時方向を変えます。
_forward(): 入力が数値の時、数字の回数だけ位置を変更します。
_toNext(): 1歩分の移動を処理します。
入力値を1文字ずつ取り出してRかLなら_changeDirection()、それ以外なら_forward()を呼びます。_forward()は領域外に出た場合falseを返すのでfalseが帰ってきた場合は終了します。
ループが終わったらpathを返します。
方向をR、Lに従って時計回り、反時計回りにdx、dyを変更します。
引数の数値回_toNext()を呼び出します。
_toNext()は移動するごとにその座標の文字を返すのでそれをpathに加えます。領域外に出た場合、'!'を返すので'!'が帰ってきた場合はfalseを返してループをやめます。
ループが最後までいったらtrueを返します。
現在の座標(x,y)から現在の方向(dx,dy)に1回だけ進め、その座標の文字を返します。
ただし、座標の文字が"="の時は三角形の右下で別の場所につながるのでその処理をします(68行目〜83行目)。これは1対1対応するので固定的に記述してしまっています。
個人的に好きなタイプの問題です。
私の方法は単純ですが、マップの形が5角形以上になると対応できません。マップが5角形以上になった場合、片ごとにマップを構築するか、1マスを表すクラスを作り、それぞれのマスのつながりを表現する必要があります。