今回は paiza の「へび」の問題に挑戦!
へびシリーズの最終Aランク問題を解くことができた!
問題概要
■ やること
- マップ上をへびが 0〜99 の間で最大 100 回移動する。
- 方向転換が指定されている時刻に向きを変え、その後 1 マス進む。
■ 移動の条件(重要)
-
移動先が
'.'(空き) のときだけ進める -
'#'(障害物) や 自分の身体'*'があると移動失敗 -
マップ外に出ても失敗
-
失敗した時点で終了し、マップを出力
■ 移動のルール
-
初期位置:与えられた (
sy,sx) -
初期向き:北(N)
-
向きは
d_iの L(左)か R(右)で回転 -
回転は指定の時刻
t_iにだけ行う -
その後、いま向いている方向に 1 マス伸びる(=移動)
■ 出力
-
最終的なマップを出力する
-
へびが通ったマスはすべて
'*'に変える -
動けなくなったらその時点で終了して出力
-
99 番目の時刻の移動まで行ったら終了
入力例:
5 5 3 1 3
.....
.....
.....
.....
.....
2 R
4 R
6 R
出力例:
.....
.***.
.*.*.
.***.
.....
✅OK例:
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', (input) => lines.push(input));
rl.on('close', () => {
const [H, W, sy, sx, N] = lines[0].split(' ').map(Number);
const gridS = lines.slice(1, H+1).map(line => line.split(''));
const moves = lines.slice(H+1).map(line => line.split(' '));
let x = sx;
let y = sy;
const turn = {
N: { L: 'W', R: 'E'},
S: { L: 'E', R: 'W'},
E: { L: 'N', R: 'S'},
W: { L: 'S', R: 'N'}
};
const dy = { N: -1, S: 1, E: 0, W: 0 };
const dx = { N: 0, S: 0, E: 1, W: -1 };
let dir = 'N';
let count = 0;
gridS[y][x] = '*';
for (let i = 0; i < 100; i++) {
if (count < N) {
const t = Number(moves[count][0]);
const d = moves[count][1];
if (i === t) {
dir = turn[dir][d];
count++;
}
}
const ny = y + dy[dir];
const nx = x + dx[dir];
if (ny >= 0 && nx >= 0 && ny < H && nx < W && gridS[ny][nx] === '.') {
gridS[ny][nx] = '*';
y = ny;
x = nx;
} else {
gridS.forEach(s => console.log(s.join('')));
return;
}
}
gridS.forEach(s => console.log(s.join('')));
});
✔️ コードの流れ(簡潔な箇条書き)
- 入力を読む準備
-
readlineで入力行を配列linesに貯める。
-
- 入力データを分解
- 1 行目から
H, W, sy, sx, Nを数値で取得。 - 次の
H行をマップとしてgridSに読み込む。 - それ以降の行を「(時刻, 回転方向)」として
movesに保持。
- 1 行目から
- 初期設定
- 現在の位置を (
x,y) にセット(スタート位置)。 - 現在向きを
'N'に設定。 - 方向転換表
turnと、各向きの移動量dy,dxを準備。 - スタート地点に
'*'を置く(へびの身体にする)。 - 次に読む方向転換のインデックス
count = 0。
- 現在の位置を (
- 最大 100 回の移動ループ開始
-
i = 0 〜 99で順番に時刻を進める。
-
- 時刻
iが方向転換のタイミングなら向きを変える-
moves[count]にまだ残りがあれば、 - その時刻
tと回転方向dを取り出す。 -
i === tならdir = turn[dir][d]で向きを変更。 -
count++して次の指示へ進む。
-
- 新しい位置 (
ny,nx) を計算- 現在向き
dirに応じて 1 マス進んだ位置を求める。
- 現在向き
- 進めるかチェックして移動 or 終了
- 範囲内かつ
gridS[ny][nx] === '.'なら進める。 - そこを
'*'にして身体を伸ばす。 - (
x,y) を (nx,ny) に更新。 - それ以外(壁・身体・範囲外)なら 終了してマップ出力。
- 範囲内かつ
- 100 回移動し終わったら最終マップを出力
📝まとめ
🔍 1. 今回の問題のポイント
-
「へびの身体が残り続ける」=踏んだマスに
'*'を残す -
「自分の身体にも衝突する」ようになる
→ 一度踏んだ場所には二度と入れない!
🔍 2. 時刻管理のコツ
-
i= 0〜99 をループ
方向転換の行は 時刻昇順で来る(t_i < t_(i+1))
→ count で次の転換を管理しやすい
🔍 3. 向き管理は表でやると超ラク
turn = {
N: {L: 'W', R: 'E'},
S: {L: 'E', R: 'W'},
E: {L: 'N', R: 'S'},
W: {L: 'S', R: 'N'}
}
🔍 4. 移動処理の順序が超重要
-
時刻が方向転換のタイミングなら向きを変える
-
次の移動先 (
ny,nx) を計算 -
範囲外・障害物・身体 → 即終了
-
そうでなければ進んで
'*'を置く
🔍 5. へびの身体は消えない(伸びっぱなし)
-
普通のスネークみたいに尻が消えない
-
常に進んだマスを
'*'にするだけ -
そのため「身体にぶつかる」チェックが増える
🔍 6. 終了条件は2種類
-
100 回(時刻 99)まで移動が終わった
-
移動不可(壁 or 身体 or 範囲外)