俄罗斯方块

保留方块图案的四列定位,逐行试探下落到首次碰撞前的位置。

OJ: shumeng

题目 ID: CSP201604B

难度:入门

标签:模拟二维数组

日期: 2026-07-31 16:21

形式化题目

给定一个 15×1015 \times 10 的初始方格图、一个 4×44 \times 4 的板块图案(其中恰好 4 格为 1)以及板块起始列号,模拟板块竖直下落并停在首次无法继续下移的位置,输出最终方格图。本题不处理消行。

思路

板块只做竖直下落,形状和列位置全程不变,因此只需确定落定的行号。

定位板块

输入给出的列号是“图案最左边一列”所在的列,即使这一列全为 0 也不能裁去图案。把图案的左上角对齐该列,用一个变量 top 记录图案当前所在的行。

试探下落

每次尝试把 top 加 1,检查图案中所有值为 1 的格子:

  • 下移后行号超过棋盘底部;
  • 或者与棋盘上已有的方块重叠。

只要出现任何一种情况,板块就不能再下落,top 保持为当前值。否则更新 top 并继续。

下图示意板块从 top 行尝试下移一行的检查(X 为板块实心格):

text
下移前 top 行                试探下移一行
. . . . . . .               . . . . . . .
. . . . . . .               . . . . . . .
. . . . . . .               . . . X X X .   <- 新位置,目标格必须为空且不越界
. . . X X X .               . . . . . X .
. . . . . X .               . . . . . . .

右侧新位置的每个 X 都要落在空且不越界的格子里,才能把 top 更新为 top + 1

写入棋盘

落定后,把板块中所有值为 1 的格子写入棋盘,最后按行输出整个棋盘。

代码

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-31 16:21
 * update_at: 2026-08-17 22:48
 */
#include <bits/stdc++.h>
using namespace std;

const int ROWS = 15;
const int COLS = 10;

int board[ROWS][COLS]; // 方格图,1 表示该格已有方块
int block[4][4];       // 下落板块图案,1 表示该格有方块

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            cin >> board[i][j];
        }
    }
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            cin >> block[i][j];
        }
    }

    int start_column;
    cin >> start_column;
    start_column--; // 输入列号从 1 开始,转为 0 基下标

    // 从第 0 行开始,每次试探下移一行:板块所有实心格都要仍能放下。
    int top = 0;
    while (true) {
        bool can_fall = true;
        for (int i = 0; i < 4 && can_fall; i++) {
            for (int j = 0; j < 4; j++) {
                if (block[i][j] == 0) {
                    continue;
                }
                int row = top + 1 + i;
                int column = start_column + j;
                // 下移一行后越界,或与已有方块重叠,都不能再下落
                if (row >= ROWS || board[row][column]) {
                    can_fall = false;
                    break;
                }
            }
        }
        if (!can_fall) {
            break;
        }
        top++;
    }

    // 把板块写入最终落定位置
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            if (block[i][j]) {
                board[top + i][start_column + j] = 1;
            }
        }
    }

    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            if (j > 0) {
                cout << ' ';
            }
            cout << board[i][j];
        }
        cout << '\n';
    }
    return 0;
}

复杂度

  • 时间:每次下落试探扫描 16 个格子,最多下落到第 15 行,时间复杂度为 O(15×16)O(15 \times 16),可视为常数。
  • 空间:棋盘与板块数组大小固定,空间复杂度为 O(1)O(1)

总结

下落判断只检查板块中值为 1 的格子,值为 0 的格子不影响碰撞。起始列对应图案最左一列,即使图案左侧为全 0 也要保留该列定位。题目不要求消行,落定后直接输出即可。