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!🚀

相关推荐
充值修改昵称几秒前
数据结构基础:二叉树高效数据结构的奥秘
数据结构·python·算法
2501_9445264212 分钟前
Flutter for OpenHarmony 万能游戏库App实战 - 笑话生成器实现
android·javascript·python·flutter·游戏
程序媛徐师姐15 分钟前
Python基于人脸识别的社区签到系统【附源码、文档说明】
python·人脸识别·python人脸识别·python社区签到系统·python人脸识别社区签到·人脸识别社区签到系统·社区签到系统
deephub29 分钟前
使用 tsfresh 和 AutoML 进行时间序列特征工程
人工智能·python·机器学习·特征工程·时间序列
0思必得035 分钟前
[Web自动化] Selenium中Select元素操作方法
前端·python·selenium·自动化·html
Duang007_39 分钟前
【万字学习总结】API设计与接口开发实战指南
开发语言·javascript·人工智能·python·学习
我爱娃哈哈40 分钟前
SpringBoot + MinIO + 阿里云 OSS:文件上传下载、分片断点续传全链路方案
spring boot·后端·阿里云
小北方城市网42 分钟前
JVM 调优实战指南:从问题排查到参数优化
java·spring boot·python·rabbitmq·java-rabbitmq·数据库架构
RunsenLIu44 分钟前
基于Spring Boot + Vue的图书馆座位预约管理系统
vue.js·spring boot·后端
shhpeng44 分钟前
go mod vendor命令详解
开发语言·后端·golang