今回は paiza の「幅のある移動」の問題に挑戦!
問題概要
✔ 問題の目的
マップ上を移動し、通ったマスを '*' に書き換えて最終マップを出力する。
✔ 与えられる情報
- マップサイズ:
H行 ×W列 - マップ内容:
-
.→ 移動可能 -
#→ 障害物(通れない)
-
- 開始位置:(
sy,sx) ※必ず. - 移動回数
N -
N個の移動指示-
d_i→ L or R(左/右回転) -
l_i→ 移動マス数(最大20)
-
✔ 移動ルール
- 初期の向きは 北(N)。
- L/R によって向きが変わる。
- 各指示ごとに、向きを変えてから
l_iマス進む。 - 移動中に
- マップ外へ出る
- 障害物
#に当たる
→ そこまでの移動分だけ'*'を付けて 全ての移動を打ち切る。
- 最初の位置も
'*'にする。
✔ 移動可能の定義
移動先が
- マップ範囲内
-
.(障害物ではない)
の両方を満たす場合。
✔ 出力
- 移動経路に
'*'が付いたH行のマップ
✔ 入力される値
H W sy sx N
S_0
...
S_(H-1)
d_1 l_1
...
d_N l_N
入力例:
10 10 4 5 3
.......#..
..........
..........
#.........
..........
......#...
..........
....#.....
...#......
..........
L 3
R 1
R 3
出力例:
.......#..
..........
..........
#.****....
..****....
......#...
..........
....#.....
...#......
..........
✅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';
gridS[y][x] = '*';
// 移動処理
for (let i = 0; i < N; i++) {
const d = moves[i][0]; // 左右の回転
const l = Number(moves[i][1]); // 移動回数
// 方向の更新
dir = turn[dir][d];
// 移動
for (let j = 0; j < l; j++) {
const ny = y + dy[dir];
const nx = x + dx[dir];
if (ny < 0 || ny >= H || nx < 0 || nx >= W || gridS[ny][nx] === '#') {
gridS.forEach(s => console.log(s.join('')));
return;
} else {
gridS[ny][nx] = '*';
}
y = ny;
x = nx;
}
}
gridS.forEach(s => console.log(s.join('')));
});
🧭 コード全体の流れ
① 入力の読み取り
- 標準入力を
lines[]にすべて読み込む - 1行目から
H, W, sy, sx, Nを取得 - 次の
H行をマップとしてgridSに2次元配列で格納 - 残りの
N行を移動指示(方向回転 + 移動距離)としてmovesに格納
② 初期設定
- 今の座標を (
x,y) にセット(sx,sy) - 初期向きは 北(N)
- 現在地
gridS[sy][sx]を'*'に変更(通った場所マーク)
③ 回転ルールの定義
- 方向と L/R 回転の対応表
turnを用意 - 例:北で L → 西、北で R → 東
④ 移動量(dx, dy)の定義
- 各方向(N, S, E, W)に対して
y,xがどう変化するか定義
⑤ N回の移動を順に処理
- 移動指示
d_i(L or R)とl_i(歩数)を取り出す - 現在の向きを更新(
turn[dir][d]) - 歩数
l回だけ繰り返し移動- 次の座標
ny,nxを計算 - 壁(
#)か、範囲外なら →
これまで歩いた分だけ'*'をつけた状態で出力して終了(以降の移動は無視) - 通れるなら
'*'を付けて座標更新
- 次の座標
⑥ 全移動が正常に終了したら
- 最終的なマップをすべて出力
📝まとめ
① 方角の管理(N, S, E, W の向きと回転)
- L/R で向きがどう変わるかの回転辞書を用意する
- 向きを決める → 動く
② 座標更新(dx, dy の使い方)
- 向きに応じて (
y,x) をどのように増減させるか整理する。 -
dx,dyを辞書(オブジェクト)で持つことでコードがシンプルになる。
③ マップ範囲チェック
0 ≤ ny < H-
0 ≤ nx < W
範囲外は即終了条件。
④ 障害物チェック
-
grid[ny][nx] === '#'のとき進めない。
⑤ “できるところまで移動して終了” の処理
- 1歩ずつ進み、途中で壁/範囲外に当たれば即終了。
- "その手前まで"
'*'を付ける点が重要。
⑥ 進んだマスを '*' に書き換える
- 開始地点も必ず
'*' - 移動中に通ったマスすべて
'*'
⑦ 全移動が終わる or 途中終了したらマップを出力
-
grid.forEach(row => console.log(row.join('')));
で二次元配列を出力。