今回は paiza の「【マップの扱い 4】マップのナンバリング」の問題に挑戦!
マップ問題の最終問題で、今までのより解きごたえがあった!
問題概要
〇 与えられるもの
- マップの 行数
Hと 列数W - ナンバリングの方向
D(1~4の整数)
〇 目的
- マップの左上 (0,0) から順に番号を振る。
- 番号の振り方は
Dの値によって異なる方向。 - 出力として
H×Wの番号付きマップ を出力する。
〇 座標
- 左上が (0,0)
- 下方向が
yの正方向、右方向がxの正方向
〇 ナンバリング方向(Dの定義)
-
D = 1→ 右上 ↗- 斜めに下から上へ番号を振る
-
D = 2→ 右 →- 行ごとに左から右へ番号を振る
-
D = 3→ 下 ↓- 列ごとに上から下へ番号を振る
-
D = 4→ 左下 ↙- 斜めに上から下へ番号を振る
入力例:
4 4 1
出力例:
1 3 6 10
2 5 9 13
4 8 12 15
7 11 14 16
↓ 図があった方がわかりやすいので、paiza のサイトを見てみて!
✅ OK例:
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', (input) => lines.push(input));
rl.on('close', () => {
const [H, W, D] = lines[0].split(' ').map(Number);
// 2次元配列を用意
const map = Array.from({ length: H }, () => Array(W).fill(0));
let num = 1;
if (D === 2) {
// 横方向 →
for (let y = 0; y < H; y++) {
for (let x = 0; x < W; x++) {
map[y][x] = num++;
}
}
} else if (D === 3) {
// 縦方向 ↓
for (let x = 0; x < W; x++) {
for (let y = 0; y < H; y++) {
map[y][x] = num++;
}
}
} else if (D === 1) {
// 斜め方向 右上 ↗
for (let s = 0; s <= H + W - 2; s++) {
for (let y = H - 1; y >= 0; y--) {
let x = s - y;
if (0 <= x && x < W) {
map[y][x] = num++;
}
}
}
} else if (D === 4) {
// 斜め方向 左下 ↙
for (let s = 0; s <= H + W - 2; s++) {
for (let y = 0; y < H; y++) {
let x = s - y;
if (0 <= x && x < W) {
map[y][x] = num++;
}
}
}
}
// 出力
map.forEach(row => console.log(row.join(' ')));
});
〇 2次元配列の準備
-
H×Wのマップを0で初期化
〇 番号振り用のカウンタ
-
num = 1からスタート
〇 D に応じた番号振り分け
-
D=2(右 →):行ごとに左から右へ -
D=3(下 ↓):列ごとに上から下へ -
D=1(右上 ↗):斜めのラインごとに下から上へ-
y + x = sのマスを1つのグループとして処理
-
-
D=4(左下 ↙):斜めのラインごとに上から下へ- 同じく
y + x = sのマスを処理
- 同じく
〇 出力
- 2次元配列を行ごとにスペース区切りで表示
🔹 1. D = 2(右 →)
普通の「横書き」。
左上 (0,0) から始めて、右に進む。行の右端まで行ったら次の行の左端へ。
例: H=3, W=4
1 2 3 4
5 6 7 8
9 10 11 12
コードの対応部分:
for (let y = 0; y < H; y++) {
for (let x = 0; x < W; x++) {
grid[y][x] = num++;
}
}
🔹 2. D = 3(下 ↓)
「縦書き」。
左上 (0,0) から始めて、下に進む。列の下端まで行ったら、次の列の上端へ。
例: H=3, W=4
1 4 7 10
2 5 8 11
3 6 9 12
コードの対応部分:
for (let x = 0; x < W; x++) {
for (let y = 0; y < H; y++) {
grid[y][x] = num++;
}
}
🔹 3. D = 1(右上 ↗)
「斜め右上方向」に番号を振る。
📌 処理は、y + x が同じ値のマスは同じ斜め(↗方向の線)に並んでいることを使う。
そのとき、下側から上に進むのが D=1 の特徴。
📌 詳しく → 各マス(座標)に y+x を書いてみると:(H=3, W=4)
(0,0)=0 (0,1)=1 (0,2)=2 (0,3)=3
(1,0)=1 (1,1)=2 (1,2)=3 (1,3)=4
(2,0)=2 (2,1)=3 (2,2)=4 (2,3)=5
グループごとに見ると(s = y + x):
s=0 → {(0,0)}
s=1 → {(0,1), (1,0)}
s=2 → {(0,2), (1,1), (2,0)}
s=3 → {(0,3), (1,2), (2,1)}
s=4 → {(1,3), (2,2)}
s=5 → {(2,3)}
これが「斜めのグループ分け」になっている。
例: H=3, W=4
1 3 6 10
2 5 9 11
4 8 12 13
コードの対応部分:
for (let s = 0; s <= H + W - 2; s++) {
for (let y = H - 1; y >= 0; y--) { // 下から上へ
let x = s - y;
if (0 <= x && x < W) {
grid[y][x] = num++;
}
}
}
① for (let s = 0; s <= H + W - 2; s++)
-
s = y + xの「斜めのグループ番号」を表している。 - 例えば
s=2のとき、(2,0),(1,1),(0,2)が候補になる。 -
sの範囲は 最小 = 0(左上)〜最大=(H-1)+(W-1) まで。 - なので
s <= H+W-2
👉 「今どの斜めのグループを処理しているか」を表すループ
② for (let y = H - 1; y >= 0; y--)
-
D=1は下から上に読むので、yを大きい方から小さい方へ動かす。 - これにより (
y,x) の順番が 下→上になる。
👉 「グループ内の並び順」を決めている
③ let x = s - y;
- 対角線の式
s = y + xを変形してx = s - y。 - つまり「今の
yに対応するxの位置」を計算。
👉 y を決めたら、その斜め上にある x が自動的に決まる
④ if (0 <= x && x < W)
-
xが範囲外になったらスキップ。 - 例えば (
y=2,s=0) →x=-2になるけどこれは盤面外。
👉 盤面の範囲チェック
⑤ grid[y][x] = num++;
- 斜めの走査順に、下から上に番号を振っていく。
💡 全体の流れ(D=1 の場合)
-
s=0→(0,0)→1 -
s=1→(1,0)=2→(0,1)=3 -
s=2→(2,0)=4→(1,1)=5→(0,2)=6 - … という具合に「右上 ↗」に伸びる番号付けになる。
🔹 4. D = 4(左下 ↙)
これも「斜め」だが、今度は 上から下に進む。
D=1と同じように処理。
例: H=3, W=4
1 2 4 7
3 5 8 10
6 9 11 12
コードの対応部分:
for (let s = 0; s <= H + W - 2; s++) {
for (let y = 0; y < H; y++) { // 上から下へ
let x = s - y;
if (0 <= x && x < W) {
grid[y][x] = num++;
}
}
}
📝 まとめ
〇 マップは2次元配列で扱う
-
H×Wの配列を用意して番号を格納する
〇 番号振りは方向 D によってループ順序を変える
-
D=2(右 →):行ごとに左から右 -
D=3(下 ↓):列ごとに上から下 -
D=1(右上 ↗):斜めのラインごとに下から上 (y + x = s) -
D=4(左下 ↙):斜めのラインごとに上から下 (y + x = s)
〇 斜めの考え方
- 同じ
y + xの値を持つマス(座標)が斜めに並んでいるを1つのグループとして扱う -
D=1は下から上、D=4は上から下に処理することで向きが変わる
〇 番号カウンタを使って順番に番号を振る
-
num = 1からスタートして順に加算
〇 出力は行ごとにスペース区切りで表示