NG SWE · VO Coding

Databricks 方块下落:固定横向 Offset 的碰撞步数与落地棋盘

把方块与障碍表示成占用格,逐行检测下一步碰撞,返回落地前移动数并合成最终棋盘。

题目重点:把方块与障碍表示成占用格,逐行检测下一步碰撞,返回落地前移动数并合成最终棋盘。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练版 board 与 piece 都为 0/1 矩阵,piece 固定朝向,offset 是左边界列,方块从 board 上方 top=-piece_height 开始每次下移一行;不旋转、不消行。
  • 宽度必须整体放入棋盘;如果遇障碍使任何填充格尚在棋盘上方就停止,训练版视作无法完全放入并报错。题目未明确这些接口边界。

解题路线

  1. 预先提取 piece 内填充格的相对坐标。定义 valid(top):所有填充格下移到 top 后不得越过底边,进入 board 的格子不得与已有障碍重合;board 上方的格子暂可存在。
  2. 从出生高度逐行尝试 top+1;只要有效就移动并计步,下一步无效就停。不要直接找所有合法 top 的最大值,因为一个方块穿过障碍后下面可能有空洞但它不能‘穿墙’落进去。
  3. 落地时复制棋盘,把每个填充格写入对应位置;先检查是否全部进入棋盘。不能把 piece 外接矩形内的 0 当实体障碍。
  4. 若题面把步数定义为从第一行可见位置算、支持旋转或消行,状态与输出都要改;本文只复原可见的固定横向 offset/下落主线。

手动走一遍

3×3 空棋盘,单格 piece=[[1]],offset=1:出生 top=-1,下移到 0/1/2 三步,下一步越底,返回 3 且最终中下格置 1。若 (1,1) 已有障碍,则只能下移到 top=0 一步,下一步碰撞,最终 (0,1) 置 1。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

"""Training version of visible falling-block prompt.
Board and piece are rectangular 0/1 matrices. The piece has fixed orientation
and fixed horizontal offset. Spawn top is -piece_height; count each one-row
translation until the next translation collides or crosses the bottom.
"""


def drop_piece(board: list[list[int]], piece: list[list[int]], offset: int) -> tuple[int, list[list[int]]]:
    if not board or not board[0] or any(len(row) != len(board[0]) for row in board):
        raise ValueError('board must be a nonempty rectangle')
    if not piece or not piece[0] or any(len(row) != len(piece[0]) for row in piece):
        raise ValueError('piece must be a nonempty rectangle')
    rows, cols = len(board), len(board[0])
    height, width = len(piece), len(piece[0])
    if offset < 0 or offset + width > cols or not any(value for row in piece for value in row):
        raise ValueError('piece must fit horizontally and contain a filled cell')
    if any(value not in (0, 1) for row in board + piece for value in row):
        raise ValueError('cells must be 0/1')
    filled = [(r, c) for r in range(height) for c in range(width) if piece[r][c]]

    def valid(top: int) -> bool:
        for pr, pc in filled:
            br, bc = top + pr, offset + pc
            if br >= rows or (br >= 0 and board[br][bc]):
                return False
        return True

    top, steps = -height, 0
    while valid(top + 1):
        top += 1
        steps += 1
    if any(top + pr < 0 for pr, _ in filled):
        raise ValueError('piece blocked before fully entering board')
    result = [row[:] for row in board]
    for pr, pc in filled:
        result[top + pr][offset + pc] = 1
    return steps, result

复杂度

棋盘 R×C、方块 H×W、填充格 P≤HW:每行尝试检查 P 格,至多 R+H 次,时间 O(P(R+H)+RC)(包含输出复制),空间 O(P+RC)。可预计算每列方块底轮廓/障碍最高点优化,但稀疏形状与洞需谨慎。

易错点与追问

容易失分的边界

  • 只查 piece 外接矩形,空格也算碰撞。
  • 取全局最深空位而允许方块穿过障碍。
  • 横向 offset 越界不检查。
  • 输出时修改原 board 导致测试间污染。
  • 出生高度/步数口径不问清就把训练答案当原题答案。

继续深挖

  • 若 piece 可旋转,状态如何增加?
  • 多个 piece 连续下落如何更新棋盘?
  • 若要消满行,落地后有哪些状态变化?
  • 如何用轮廓快速求第一碰撞高度?
  • piece 一部分卡在棋盘上方时怎样定义 game over?
✓已复制微信号!