🎄 Advent of Code 2025 挑战全手写代码 Day 11 - 反应堆
大家好,昨天(day 10)是不是也觉得很有挑战?今天 day 11 轻松一点了!今日题目 Reactor 难度中等(三星 ⭐⭐⭐),主要考察有向无环图(DAG)、路径计数、递归、深度优先搜索(DFS) 等知识点。
📖 题目速览
- 题目地址:adventofcode.com/2025/day/11
- 背景:我们来到了反应堆维护室,需要帮助小精灵修复设备连接。
- 输入格式:一系列设备及其连接关系,例如 aaa: bbb ccc ,表示设备 aaa 指向 bbb 和 ccc 。这构成了一个有向图。题目中提到数据只能顺流而下("can't flow backwards"),暗示这是一个有向无环图(DAG)。
第一部分:计算从起点 "you" 到终点 "out" 的所有不同路径的数量。
第二部分:计算从起点 "svr" 到终点 "out" 的所有路径中,既经过节点 "dac" 又经过节点 "fft" (谁先谁后都可以)的路径数量。
🧠 解题思路 (Python 🐍)
Part 1:
- 从起点
"you"开始,使用深度优先搜索(DFS)遍历所有可能的路径。 - 每次遍历到一个节点时,检查是否到达终点
"out"。如果是,记录一条路径。 - 最后返回记录的路径数量。
递归部分代码如下:
def _count_paths(self, source: str) -> int:
if "out" in self.connections[source]:
return 1
out: int = 0
for key in self.connections[source]:
out += self._count_paths(source=key)
return out
使用
self._count_paths(source="you")
即可求得从 "you" 到 "out" 的所有不同路径的数量。
Part 2:
- 若遍历所有 svr 到 out 的路径,再记录经过 dac 和 fft 的路径数,既效率低下,也没有必要。
- 由于必须经过两个特定中间点,且顺序不限,路径只可能是以下两种情况之一:
- svr -> ... -> dac -> ... -> fft -> ... -> out
- svr -> ... -> fft -> ... -> dac -> ... -> out
- 我们可以复用第一部分的路径计数逻辑,编写一个通用的 count_paths(start, end) 函数。
- 情况 1 的路径数 = count_paths('svr', 'dac') * count_paths('dac', 'fft') * count_paths('fft', 'out')
- 情况 2 的路径数 = count_paths('svr', 'fft') * count_paths('fft', 'dac') * count_paths('dac', 'out')
- 最终结果为这两种情况之和。
可将 part 1 中的递归函数改造为:
def _count_paths(self, source: str, target: str) -> int:
if source == "out":
return 0
if target in self.connections[source]:
return 1
out: int = 0
for key in self.connections[source]:
out += self._count_paths(source=key, target=target)
return out
使用
self._count_paths(source="svr", target="dac")
即可求得从 "svr" 到 "dac" 的所有不同路径的数量。
以此类推,即可求得其它节点之间的路径数量。
然而这种解法虽然在示例数据上能够通过,但在实际数据上会超时。
这是因为在递归过程中,会重复计算一些路径。
例如,从 "svr" 到 "dac" 的路径数,会被计算多次。
为了避免重复计算,我们可以使用 Memoization 技术,即缓存已经计算过的路径数。
一种思路是使用 Python 标准库中的 functools.cache 装饰器(LRU 缓存),记忆已经计算过的路径数。一旦计算过某个节点的路径数,下次直接返回结果,将时间复杂度从指数级降低到线性 。
import functools
def count_paths(self, start: str, end: str) -> int:
"""
Count all paths from start to end using DFS with memoization.
"""
@functools.cache
def dfs(current: str) -> int:
if current == end:
return 1
if current not in self.connections:
return 0
total_paths = 0
for neighbor in self.connections[current]:
total_paths += dfs(neighbor)
return total_paths
return dfs(start)
结语
今天题目的核心在于实现一个高效的“两点间路径计数”函数。只要图是无环的,通过简单的 DFS + Memoization 即可完美解决。 今天题目难度下降,应该不会花费太多力气就可以拿到两颗星星⭐⭐ 明天(Day 12)是最后一天了,坚持就是胜利!记得继续冲榜!🚀