Advent of Code 2025 挑战全手写代码 Day 4 - 印刷部门

0 阅读3分钟

🎄Advent of Code 2025 挑战全手写代码 Day 4 - 印刷部门

大家好,今天咱们继续更新 Advent of Code 2025 的解题记录。

第四天题目 Printing Department,难度 ⭐(1 星),主要考察 矩阵模拟(simulation)

📖 题目速览

题目地址:adventofcode.com/2025/day/4

advent-of-code-2025-day-4

背景

我们坐扶梯下到印刷部,为了打破一堵墙进入自助餐厅,我们需要指挥叉车搬运成堆的纸卷(用 @ 表示),这样闲下来的叉车就能帮我们打破墙壁了。

Part 1: 叉车的作业规则

题目给出一个由 . (空地) 和 @ (纸卷) 组成的网格。 叉车搬运纸卷有一个限制:只有当一个纸卷周围 8 个方向(上下左右及对角线)的纸卷数量少于 4 个时,叉车才能接触并搬走它。

Part 1 的问题很简单:当前状态下,有多少个纸卷可以被叉车叉走?

Part 2: 连锁反应

Part 2 引入了时间维度的模拟。 一旦一个纸卷被移除,它原来的位置就变成了空地。这可能会导致原本因为拥挤而无法被接触到的内部纸卷,在周围变空后,变得可以被接触到了。 这是一个重复的过程:

  1. 找出所有当前可以移除的纸卷。
  2. 移除它们。
  3. 重复步骤 1,直到没有任何纸卷可以被移除。

问题是:总共能移除多少个纸卷?


解题思路

数据结构

直接使用二维列表 list[list[str]] 来存储网格即可。内层使用list而不是一整条str是因为在 part 2 我们需要对网格进行写操作。

核心逻辑:检查邻居

无论是 Part 1 还是 Part 2,核心都是判断一个坐标 (x, y) 是否满足“周围 @ 数量 < 4”。

def _can_fork(self, idx: int, jdx: int) -> bool:
    """检查该位置的纸卷是否可以被叉车接触"""
    adjacent_coordinates = (
        (idx - 1, jdx - 1), (idx, jdx - 1), (idx + 1, jdx - 1),
        (idx - 1, jdx),                     (idx + 1, jdx),
        (idx - 1, jdx + 1), (idx, jdx + 1), (idx + 1, jdx + 1),
    )

    count = 0
    for x, y in adjacent_coordinates:
        # 边界检查
        if 0 <= x < len(self.grid[0]) and 0 <= y < len(self.grid) and self.grid[y][x] == "@":
                count += 1

    return count < 4

Part 1 实现

直接遍历整个网格,对每个 @ 调用 _can_fork,计数即可。


Part 2 的踩坑与反思

❌ 错误的尝试:检查过程中的逻辑错误

一开始我为了图省事,写出了类似这样的逻辑:

    # 错误示范
    def _can_fork(self, idx: int, jdx: int) -> bool:
        """check if the cell can fork"""

        adjacent_coordinates = ...
        out: int = 0

        for x, y in adjacent_coordinates:
            if 0 <= x < len(self.grid[0]) and 0 <= y < len(self.grid):
                if self.grid[y][x] == "@":
                    self.grid[y][x] = "."  # <--- 问题出在这里!
                    out += 1

        return out <= 3

为什么这是错的? 本来应该将符合条件的目标位置改为".",但忙中出错,反而将目标周围的@修改了,这导致每一轮中都有纸卷被错误地标记为“移除”,从而出现运行时的死循环。

✅ 正确的实现

正确的做法是只更改目标位置的纸卷:

    def part2(self) -> int:
        total: int = 0

        while True:
            single_round: int = 0

            for jdx, row in enumerate(self.grid):
                for idx, char in enumerate(row):
                    if char == ".":
                        continue
                    if self._can_fork(idx, jdx):
                        self.grid[jdx][idx] = "."  # <- 就是这里
                        single_round += 1

            if not single_round:
                break

            total += single_round

        return total

总结

Day 4 本来难度不高,但因为一开始急于求解,反而造成了逻辑错误,我要反思(doge

完整代码:访问 github

Happy Coding,今天也要继续冲榜~

day-4-ranking