今回は paiza の「リバーシの操作」の問題に挑戦!
問題概要
- 縦
H行、横W列の盤面が与えられる - 盤面には
-
'*':すでに置かれている自分の石 -
'.':何も置かれていないマス
が存在する
-
- 指定されたマス (
Y,X) は必ず'.'であり、ここに 新しく石を1つ置く
操作ルール
- 石を (
Y,X) に置いたあと、 - 縦・横・斜めの 8 方向 をそれぞれ調べる
- ある方向について
-
'*' → '.' → '.' → … → '*'
のように
両端が'*'ではさまれている場合、その間の'.'をすべて'*'にする
-
- 途中で
- 盤面の外に出る
-
'*'に到達できない
場合は、その方向は何もしない
- 新たに置いた石によってさらに石が置ける場合があっても操作は 1 回で終了
操作後の盤面を 上から順に H 行出力 する
入力例:
3 3 0 0
..*
...
*.*
出力例:
***
**.
*.*
✅OK例:
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', (line) => lines.push(line));
rl.on('close', () => {
const [H, W, Y, X] = lines[0].split(' ').map(Number);
const grid = lines.slice(1).map(row => row.split(''));
// 8方向
const directionList = [
[-1, 0], // 上
[-1, 1], // 右上
[0, 1], // 右
[1, 1], // 右下
[1, 0], // 下
[1, -1], // 左下
[0, -1], // 左
[-1, -1] // 左上
];
grid[Y][X] = '*'; // 石を置く
for (const direction of directionList) {
const [dy, dx] = direction;
let ny = Y + dy;
let nx = X + dx;
const path = []; // この方向で通過したマス
// 盤面内を進む
while (0 <= ny && ny < H && 0 <= nx && nx < W) {
if (grid[ny][nx] === '*') {
// はさめた → 間をすべて '*'
for (const [py, px] of path) {
grid[py][px] = '*';
}
break;
}
path.push([ny, nx]); // 通過マスを保存
ny += dy; // 次のマスへ
nx += dx; // 次のマスへ
}
}
// 盤面出力
grid.forEach(g => console.log(g.join('')));
});
🔍 コード全体の流れ
- 入力をすべて
lines配列に読み込む - 1行目から
- 盤面サイズ
H,W - 石を置く座標 (
Y,X)
を取得する
- 盤面サイズ
- 続く
H行から盤面をgrid(2次元配列)として作成する
石を置いて挟む処理
- 縦・横・斜めの 8方向 を配列で用意する
- 指定されたマス
(Y, X)に石'*'を置く - 8方向それぞれについて以下を行う
- その方向に 1マス先 から探索を開始する
- 進んだマスを
pathに順番に保存する - 盤面の外に出るまで繰り返す
-
'*'にぶつかったら
→ はさめたと判断し、pathにあるマスをすべて'*'にする
→ その方向の探索を終了 -
'*'でなければ
→ そのマスをpathに追加して次へ進む
-
出力
- すべての方向の処理が終わった後の盤面を各行ごとに文字列として出力する
📝まとめ
- Step5 は Step2(縦横)+ Step4(斜め) の統合版
- すべての方向は
(dy, dx)の配列で管理できる- 上・下・左・右
- 右上・右下・左下・左上
- 実装パターンは全方向共通
- 1マス先から探索開始
- 通過マスを
pathに一時保存 -
'*'に到達したら
→pathをまとめて'*'にする