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

相关推荐
云浪19 分钟前
Go 堆实战:从完全二叉树到 Top-K 与任务调度
后端·go
东莞市云毅网络有限公司6 小时前
AI 引用句逐条回指原文:让回答可回溯的校验实现
python·数据清洗·rag·企业知识库·文档解析
数字融合8 小时前
透明化视频三维矿山井下照明重建技术
人工智能·python·数码相机
yi0118 小时前
LeetCode 219:存在重复元素 II——哈希表记录“最近一次出现的位置”
数据结构·人工智能·笔记·python·算法·leetcode·哈希表
Marst Code9 小时前
上位机开发日记 · 第 2 篇 · 架构先行:六层分层与边界
python
刚子编程10 小时前
我用 ASP.NET Core 做了个水稻病虫害检查系统
后端·asp.net
李日华大战鸡红10 小时前
FOC状态空间方程模型推导(学习记录)
python·学习·线性代数
谢亮_vipxieliang10 小时前
用 PHP 构建轻量级 REST API:从路由到鉴权的完整实践
开发语言·后端·php
外收内放12 小时前
Python基础语法练习题(57-58)
开发语言·python