今回は paiza の「【マップの扱い 3】マップの判定・縦横斜め」の問題に挑戦!
前と違うのは、周囲1マスの縦横斜めではなくて、縦横斜めのラインすべてを書き換えること!
問題概要
〇 入力
- 盤面の行数
H
と列数W
- 盤面
S_0 … S_(H-1)
- 操作対象の座標
y
,x
〇 処理内容
- 指定座標
(y, x)
と 同じ縦・横・斜めにあるすべてのマス を対象とする - 対象マスの文字を書き換える
-
.
→#
-
#
→.
-
- 座標系は左上を
(0,0)
、下方向がy
、右方向がx
〇 出力
- 処理後の盤面 H 行を出力
入力例:
3 3
##.
###
...
0 0
出力例:
..#
..#
#.#
❌ NG例:
// 処理する座標
const targets = new Set();
// 縦
for (let i = 0; i < H; i++) {
targets.add([i, x);
}
// 横
for (let i = 0; i < W; i++) {
targets.add([y, i]);
}
// 斜め
for (let i = 0; i < Math.max(H, W); i++) {
if (y + i < H && x + i < W) targets.add([y+i, x+i]);
if (y + i < H && x - i >= 0) targets.add([y+i, x-i]);
if (y - i >= 0 && x + i < W) targets.add([y-i, x+i]);
if (y - i >= 0 && x - i >= 0) targets.add([y-i, x-i]);
}
❌ Set
は「参照の一意性」で判定するため、例えば、[1,2]
と [1,2]
でも別物として扱われる。
よって配列のまま Set
に入れても重複を除外できない。
✅ OK例:文字列化して重複排除
const rl = require('readline').createInterface({ input:process.stdin });
const lines = [];
rl.on('line', (input) => lines.push(input));
rl.on('close', () => {
const [H, W] = lines[0].split(' ').map(Number);
const [y, x] = lines[H+1].split(' ').map(Number);
const map = lines.slice(1, H+1).map(line => line.split(''));
// 処理する座標
const targets = new Set();
// 縦
for (let i = 0; i < H; i++) {
targets.add(`${i},${x}`);
}
// 横
for (let i = 0; i < W; i++) {
targets.add(`${y},${i}`);
}
// 斜め
for (let i = 0; i < Math.max(H, W); i++) {
if (y + i < H && x + i < W) targets.add(`${y+i},${x+i}`);
if (y + i < H && x - i >= 0) targets.add(`${y+i},${x-i}`);
if (y - i >= 0 && x + i < W) targets.add(`${y-i},${x+i}`);
if (y - i >= 0 && x - i >= 0) targets.add(`${y-i},${x-i}`);
}
// 処理
for (const pos of targets) {
const [ty, tx] = pos.split(',').map(Number);
map[ty][tx] = map[ty][tx] === '.' ? '#' : '.';
}
// 出力
map.forEach(row => console.log(row.join('')));
});
-
座標を 文字列化(
${y},${x}
) してSet
に格納。 -
最後に
split(',').map(Number)
で数値に戻せば良い。 -
Math.min(H, W)
→(y, x)
が盤面の角 にある場合、斜めに移動できる距離は 最大で 縦H
か横W
のどちらか 小さい方 まで必要になる。
🗒️ まとめ
〇 盤面は2次元配列で管理
〇 重複座標の扱い
- JS の
Set
は配列やオブジェクトを参照で判定するため、
例:[1,2]
と[1,2]
は別物扱いになる
- 解決策:座標を文字列化して格納
targets.add(`${y},${x}`);
- 最後に
split(',').map(Number)
で数値に戻して処理する
〇 縦・横・斜めの列挙方法
- 縦:
for (let i=0; i<H; i++) targets.add(${i},${x})
- 横:
for (let i=0; i<W; i++) targets.add(${y},${i})
- 斜め:
for (let i=0; i<Math.max(H,W); i++)
と 境界条件チェック
〇 書き換え処理は三項演算子で簡潔に
map[ty][tx] = map[ty][tx] === '.' ? '#' : '.';
〇 出力は join
で1行ずつ