0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「落ちものシミュレーション」を解くために : part3

Posted at

今回は 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 とする
    • 探索を終了

⑥ ブロックを配置

  • bottom === -1 の場合
    → 何にも当たらないので フィールドの底に置く
  • それ以外の場合
    → 見つかったブロックの 直上に重ねて置く

⑦ フィールド更新

  • 該当する長方形範囲を "#" で塗りつぶす

⑧ 出力

  • 各行をつなげて順に console.log
  • 最終的なフィールド状態を表示






📝まとめ

① フィールドを「状態」として持つ

  • 2 次元配列で盤面を管理
  • 各操作で 直接更新

② 落下位置は「上から探索」

  • ブロックは必ず最初に当たった場所の直上に止まる
  • よって、上 → 下に調べる

bottom の考え方が重要

  • bottom = ブロックの 下辺が来る行
  • i - 1 にする理由
    • i 行目に # があった → その 1つ上

④ 底に当たる場合も同じロジック

  • bottom === -1
    → 何にも当たらなかった
    → 一番下(H - 1)を基準に置く

⑤ 長方形は「範囲塗り」

  • 行:bottom - h + 1bottom
  • 列:xx + w - 1
  • for ループ 2 重で確実に塗る
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?