今回は paiza の「「落ちものシミュレーション」を解くために : part3」の問題に挑戦!
🧩 問題概要
問題でやること
- 縦
H、横Wのフィールドに - 2 個の長方形ブロックを上から順に落とす
- すべて落下し終わった後の 最終的なフィールド状態を出力する
フィールドについて
- フィールドは
H×Wのマス目 - 各マスの状態
-
".":空 -
"#":ブロックあり
-
- 上から 1 行目が 最上段
- 下が 重力方向
落ちてくる長方形について
- 各ブロックは次の情報を持つ
-
h:高さ -
w:幅 -
x:左端の位置(0-index)
-
- ブロックは 回転しない
- 横方向には動かず、真下に落ちる
落下と固定のルール
- 落ちている途中で
- 他のブロックに接触
または - フィールドの底に接触
した瞬間に その位置で固定
- 他のブロックに接触
- 接触とは、ブロックの どこか1マスでも下に
"#"がある状態
入力
- 1 行目:
H W N(※この問題ではN = 2固定) - 2 行目:1 個目のブロック情報
- 3 行目:2 個目のブロック情報
出力
-
H行出力 - 各行は長さ
Wの文字列 - 最終的なフィールド状態を 上から順に表示
条件
- すべてのブロックは 必ずフィールド内に収まる
入力例:
7 10 2
1 8 1
4 1 5
出力例:
..........
..........
.....#....
.....#....
.....#....
.....#....
.########.
✅OK例:
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const lines = [];
rl.on('line', (line) => {
lines.push(line);
});
rl.on('close', () => {
let idx = 0;
// H, W, N
const [H, W, N] = lines[idx++].split(' ').map(Number);
// フィールド初期化
const field = Array.from({ length: H }, () => Array(W).fill('.'));
// N 回ブロックを落とす
for (let _ = 0; _ < N; _++) {
const [h, w, x] = lines[idx++].split(' ').map(Number);
// ブロックの下側の辺の行
let bottom = -1;
// ブロックを置く位置を探す
for (let i = 0; i < H; i++) {
for (let j = x; j < x + w; j++) {
if (field[i][j] === '#') {
bottom = i - 1;
break;
}
}
if (bottom !== -1) break;
}
// ブロックを配置
// 底に接触する場合
if (bottom === -1) {
for (let i = H - h; i < H; i++) {
for (let j = x; j < x + w; j++) {
field[i][j] = '#';
}
}
}
// 他のブロックに接触する場合
else {
for (let i = bottom - h + 1; i <= bottom; i++) {
for (let j = x; j < x + w; j++) {
field[i][j] = '#';
}
}
}
}
// 出力
for (let i = 0; i < H; i++) {
console.log(field[i].join(''));
}
});
🔍 コードの流れ
① 入力準備
-
readlineで 標準入力を1行ずつ受け取る - すべての入力を
lines配列に保存
② 入力読み取り開始(close 時)
-
idxを使って 入力を先頭から順に消費 -
H, W, Nを取得
→ フィールドの高さ・幅・落ちてくるブロック数
③ フィールド初期化
-
H×Wの二次元配列fieldを作成 - 全マスを
"."(空)で埋める
④ N 個のブロックを順に処理
- 各ブロックごとに
h, w, xを取得
→ 高さ・幅・左端の列
⑤ ブロックが止まる位置を探す
- 上から順にフィールドをチェック
- ブロック幅
w分のどこかに#があれば- その 1つ上の行を
bottomとする - 探索を終了
- その 1つ上の行を
⑥ ブロックを配置
-
bottom === -1の場合
→ 何にも当たらないので フィールドの底に置く - それ以外の場合
→ 見つかったブロックの 直上に重ねて置く
⑦ フィールド更新
- 該当する長方形範囲を
"#"で塗りつぶす
⑧ 出力
- 各行をつなげて順に
console.log - 最終的なフィールド状態を表示
📝まとめ
① フィールドを「状態」として持つ
- 2 次元配列で盤面を管理
- 各操作で 直接更新
② 落下位置は「上から探索」
- ブロックは必ず最初に当たった場所の直上に止まる
- よって、上 → 下に調べる
③ bottom の考え方が重要
-
bottom= ブロックの 下辺が来る行 -
i - 1にする理由-
i行目に#があった → その 1つ上
-
④ 底に当たる場合も同じロジック
-
bottom === -1
→ 何にも当たらなかった
→ 一番下(H - 1)を基準に置く
⑤ 長方形は「範囲塗り」
- 行:
bottom - h + 1~bottom - 列:
x~x + w - 1 -
forループ 2 重で確実に塗る