Advent of Code 2025 挑战全手写代码 Day 8 - 游乐场

🎄Advent of Code 2025 挑战全手写代码 Day 8 - 游乐场


新的一周继续冲刺!今日题目 Playground 难度中等(三星半 ⭐⭐⭐),核心考察:欧氏几何最短边排序/Top‑K并查集(Union‑Find) 。数据规模为 n≈1000,两两点对约 499,500,Python 基于全量构边 + 并查集可以稳定通过。

📖 题目速览

  • 题目地址:adventofcode.com/2025/day/8
  • 输入:每行一个 3D 坐标 X,Y,Z,表示接线盒位置。
  • 距离:按欧氏距离(建议使用"平方距离 "避免 sqrt)从小到大考虑"盒子对"。
  • Part 1:按全局最短的顺序处理 1000 次尝试(包括连接同一连通分量的"无效尝试")。完成后统计所有连通分量大小,取最大的三个相乘。
  • Part 2:持续连接 仅在不同分量之间 的最近一对,直到所有点归于一个分量。答案为"最后一次有效合并"两端点的 X轴 坐标相乘。

👀 题目大意

  • 有很多接线盒悬挂在地下游乐场,输入给出它们的三维坐标。
  • 精灵们要用灯串连接接线盒,让电能能在盒子之间传递。连接两盒会把它们所在的电路(连通分量)合并。
  • 需要按"距离最近的盒子对"逐步连接:
    • Part 1:严格处理 1000 条"全局最近的边",允许无效尝试(同一分量内连接不改变结构,但也要算一次)。最后输出三大分量大小的乘积。
    • Part 2:只做有效合并,直到所有盒子成为一个大电路;输出最后一次合并的两个盒子的 X 坐标乘积。

🧠 解题思路(Kruskal 视角)

  • 边构建:枚举所有点对 (i<j),计算平方距离 dist2,形成三元组 (dist2, i, j)
  • 稳定排序:存在大量相等距离时,建议用 (dist2, i, j) 作为排序键,保证确定性。
  • 数据结构:并查集(路径压缩 + 按大小合并),单次近似常数时间。
  • Part 1 流程:
    • 取全局最短的 1000 条边(可用 heapq.nsmallest 或维护大小为 1000 的最大堆 Top‑K)。
    • 按距离升序遍历这 1000 条边:如果两端已在同一分量,跳过结构更新但尝试数仍+1;否则执行 union
    • 结束后统计所有节点的分量大小(包含未被触及的单点分量),取前三相乘。
  • Part 2 流程:
    • 按距离升序扫描全边;仅当 find(i) != find(j) 时执行 union 并记录这条边为"最后有效连接"。
    • 当有效合并次数达到 n-1,所有点连通;输出"最后有效连接"的两个端点 X 的乘积。

⏱️ 复杂度分析

  • 构边:O(n^2);排序:O(n^2 log n);并查集:O(n^2 · α(n))(近似常数)。
  • n=1000 时,Python 能够在合理时间内处理 ~50 万条边。
  • 优化建议:距离用"平方距离";仅在需要时使用 Top‑K 堆减少排序成本(Part 1)。

🐞 踩坑与修正

  • Part 1 必须"计数尝试",不能只计有效合并;否则答案会偏小。
  • 相等距离的边次序不稳定会影响选取的 1000 条边,导致结果波动;用 (dist2, i, j) 排序稳定。
  • 统计分量大小时应包含所有节点(未参与任何连接的节点也是大小为 1 的分量)。
  • 注意读入的文件路径与规模:K 值应为 1000

🧩 关键代码片段(Python 3.12,标准库)

python 复制代码
import heapq
from pathlib import Path

class UnionFind:
    def __init__(self, n: int):
        self.parent = list(range(n))
        self.size = [1] * n
    def find(self, x: int) -> int:
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]
            x = self.parent[x]
        return x
    def union(self, a: int, b: int) -> bool:
        ra, rb = self.find(a), self.find(b)
        if ra == rb:
            return False
        if self.size[ra] < self.size[rb]:
            ra, rb = rb, ra
        self.parent[rb] = ra
        self.size[ra] += self.size[rb]
        return True

def squared_distance(p, q):
    return sum((p[k] - q[k]) ** 2 for k in range(3))

def solve_part1(points: list[tuple[int, int, int]]) -> int:
    n = len(points)
    # Build all edges
    edges: list[tuple[int, int, int]] = []
    for i in range(n - 1):
        for j in range(i + 1, n):
            edges.append((squared_distance(points[i], points[j]), i, j))
    # Take 1000 smallest edges
    smallest = heapq.nsmallest(1000, edges)
    uf = UnionFind(n)
    # Process attempts in ascending order
    for _, i, j in sorted(smallest):
        uf.union(i, j)  # union only if different; attempts always count
    # Compute component sizes
    comp = {}
    for i in range(n):
        r = uf.find(i)
        comp[r] = comp.get(r, 0) + 1
    top3 = sorted(comp.values(), reverse=True)[:3]
    return top3[0] * top3[1] * top3[2]

def solve_part2(points: list[tuple[int, int, int]]) -> int:
    n = len(points)
    edges: list[tuple[int, int, int]] = []
    for i in range(n - 1):
        for j in range(i + 1, n):
            edges.append((squared_distance(points[i], points[j]), i, j))
    edges.sort()  # stable: (dist2, i, j)
    uf = UnionFind(n)
    merges = 0
    last_i = last_j = 0
    for _, i, j in edges:
        if uf.union(i, j):
            merges += 1
            last_i, last_j = i, j
            if merges == n - 1:
                break
    return points[last_i][0] * points[last_j][0]

🧪 验证与表现

  • 样例(题面给出)应得到 Part 1:40;Part 2:25272
  • 真实数据:n≈1000,构边约 50 万,Python 运行在秒级,注意使用平方距离与稳定排序。

🔗 参考与代码

坚持到最后一题,冲刺就有希望!祝大家 AC 顺利,Happy Coding!🚀

相关推荐
青梅主码1 小时前
全球顶级大模型最新排名出炉:中国大模型表现优秀,DeepSeek V3.2 与 Kimi K2 Thinking 均挤进前 10
后端
刘 大 望1 小时前
JVM(Java虚拟机)
java·开发语言·jvm·数据结构·后端·java-ee
超级种码1 小时前
JVM 字节码指令活用手册(基于 Java 17 SE 规范)
java·jvm·python
SadSunset1 小时前
(3)第一个spring程序
java·后端·spring
子午1 小时前
【垃圾识别系统】Python+TensorFlow+Django+人工智能+深度学习+卷积神经网络算法
人工智能·python·深度学习
CHANG_THE_WORLD1 小时前
Python 推导式详细教程
开发语言·python
ljh5746491191 小时前
用vscode怎么运行conda中的python环境
vscode·python·conda
milanyangbo2 小时前
像Git一样管理数据:深入解析数据库并发控制MVCC的实现
服务器·数据库·git·后端·mysql·架构·系统架构
秋邱2 小时前
AR 技术创新与商业化新方向:AI+AR 融合,抢占 2025 高潜力赛道
前端·人工智能·后端·python·html·restful