Advent of Code 2025 挑战全手写代码 Day 3 - 大堂

40 阅读3分钟

🎄Advent of Code 2025 挑战全手写代码 Day 3 - 大堂


第三天题目 Lobby,难度 ⭐⭐(2 星),主要考察 贪心算法 (Greedy Algorithm)预处理优化

📖 题目速览

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

advent-of-code-2025-day-3

背景: 我们来到了大厅,发现电梯和自动扶梯都断电了。为了启动它们,我们需要利用手头的电池组(Batteries)。输入数据的每一行代表一组电池序列(数字 1-9)。

Part 1: 对于每一行电池(数字序列),我们需要从中按顺序挑选出 2 个电池,使得这两个数字组成的两位数(Joltage)最大。 例如序列 987654321,选 98 组成 98。 求所有行最大两位数之和。

Part 2: 规则升级!这次我们需要从每一行中按顺序挑选出 12 个电池,使得这 12 个数字组成的十二位数最大。 求所有行最大十二位数之和。


💡 解题思路 (Python 🐍)

这道题本质上是一个 “求字典序最大的固定长度子序列” 的问题。

Part 1: 双指针 vs 后缀最大值 (Suffix Max)

要让两位数 d1d2d_1 d_2 最大,首先 d1d_1 要尽可能大,其次 d2d_2 要尽可能大。 由于只需要选 2 个数,我们可以使用极其高效的 后缀最大值(Suffix Max) 预处理。

  1. 预处理:倒序遍历数组,记录“从当前位置到末尾”的最大值。
    • largest_seen[i] 表示 bank[i+1:] 中的最大值。
  2. 遍历:枚举每一个数作为第一个数 d1d_1,然后直接查表得到它后面最大的数作为 d2d_2
  3. 比较:在所有组合中取最大值。

这种方法的时间复杂度是严格的 O(N)O(N)

Part 2: 贪心构建 (Greedy Construction)

当需要选 12 个数时,预处理所有组合显然不可行(复杂度爆炸!)。这里我们采用 贪心策略。 因为高位数字对数值大小起决定性作用(99\dots 永远大于 88\dots),我们应该从左到右逐位确定数字,每一位都尽可能选最大的。

  1. 预处理索引:将每个数字(1-9)出现的所有索引位置记录在哈希表(dict)中,方便 O(1)O(1) 查找。
  2. 逐位构建:我们需要填 12 个坑。
    • 对于第 1 个坑,尝试填 9。如果 9 存在,且选了它之后,后面的剩余字符足够填满剩下的 11 个坑,那就选它!
    • 如果 9 不行,就试 8,以此类推。
  3. 维护区间
    • lower_bound:上一个选定数字的索引 + 1(因为必须按顺序选)。
    • upper_bound:当前能选的最远索引(保证后面剩下的数字够用)。

✨ 代码复盘 & 优化思考

在 Part 1 中,我使用了一个特定于 K=2K=2 的优化:

# Precompute the max value to the right of each index (suffix max)
largest_seen: list[int] = []
for battery in bank[::-1]:
    if not largest_seen:
        largest_seen.append(battery)
    else:
        largest_seen.append(max(battery, largest_seen[-1]))

而在 Part 2 中,为了处理 K=12K=12 这种更通用的情况,我切换到了基于索引约束的贪心搜索。这种方法的扩展性更强,如果题目改成选 KK 个数,只需要把循环次数改为 KK 即可。

# Greedily construct the 12-digit number from left to right
for position in range(1, 13):
    # Update upper bound to ensure enough digits remain
    upper_bound = len(bank) - 12 + position

    # Try digits from 9 down to 1
    for candidate in range(9, 0, -1):
        # ... find first valid index ...

即使是 Part 2 这种 12 个数字的情况,也可以轻松在1秒内完成。

完整代码:访问 github

Happy Coding! 今天也要继续冲榜!🚀 day-3-ranking