原题地址 。
— 第 8 天:游乐场 —
凭借对传送器维护的新理解,你自信地踏上了修复好的传送器平台。
你在一个陌生的传送器平台上重新实体化,发现自己身处一个巨大的地下空间,里面有一个巨大的游乐场!
在游乐场对面,一群精灵正在努力搭建一个雄心勃勃的圣诞装饰项目。通过精心布置,他们悬挂了大量的小型电气接线盒。
他们的计划是用长串的灯来连接接线盒。大多数接线盒不提供电力;然而,当两个接线盒通过一串灯连接时,电力就可以在这两个接线盒之间传递。
精灵们正试图找出哪些接线盒需要连接,以便电力能够到达每一个接线盒。他们甚至有一份所有接线盒在三维空间中位置的列表(你的谜题输入)。
例如:
162,817,812
57,618,57
906,360,560
592,479,940
352,342,300
466,668,158
542,29,236
431,825,988
739,650,466
52,470,668
216,146,977
819,987,18
117,168,530
805,96,715
346,949,466
970,615,88
941,993,340
862,61,35
984,92,344
425,690,689
此列表描述了 20 个接线盒的位置,每行一个。每个位置以 X,Y,Z 坐标给出。因此,列表中的第一个接线盒在 X=162, Y=817, Z=812。
为了节省灯串,精灵们希望专注于连接那些根据直线距离尽可能接近的接线盒对。在这个例子中,距离最近的两个接线盒是 162,817,812 和 425,690,689。
通过连接这两个接线盒,因为电力可以在它们之间流动,它们成为同一电路的一部分。连接它们之后,有一个包含两个接线盒的电路,剩下的 18 个接线盒则各自保持在自己的独立电路中。
现在,距离最近但尚未直接连接的两个接线盒是 162,817,812 和 431,825,988。连接它们之后,由于 162,817,812 已经连接到另一个接线盒,现在出现了一个包含三个接线盒的电路,以及另外 17 个各包含一个接线盒的电路。
接下来要连接的两个接线盒是 906,360,560 和 805,96,715。连接它们之后,有一个包含 3 个接线盒的电路,一个包含 2 个接线盒的电路,以及 15 个各包含一个接线盒的电路。
接下来的两个接线盒是 431,825,988 和 425,690,689。因为这两个接线盒已经在同一电路中,所以什么也不会发生!
这个过程持续了一段时间,精灵们担心他们没有足够的延长线来连接所有这些电路。他们想知道这些电路会有多大。
在进行完10 次最短连接之后,共有 11 个电路:一个包含 5 个接线盒的电路,一个包含 4 个接线盒的电路,两个各包含 2 个接线盒的电路,以及七个各包含单个接线盒的电路。将三个最大的电路(5、4 和其中一个大小为 2 的电路)的大小相乘,得到 40。
你的列表包含许多接线盒;连接1000 对距离最近的接线盒。之后,将三个最大电路的大小相乘,你会得到什么?
— 第二部分 —
精灵们是对的;他们肯定没有足够的延长线。你需要继续连接接线盒,直到它们全部在一个大电路中。
继续上面的例子,第一次使得所有接线盒形成单个电路的连接是在坐标为 216,146,977 和 117,168,530 的接线盒之间。精灵们需要知道这些接线盒距离墙壁多远,以便选择合适的延长线;将这两个接线盒的 X 坐标相乘(216 和 117)得到 25272。
继续连接距离最近的未连接接线盒对,直到它们都在同一个电路中。如果将最后需要连接的两个接线盒的 X 坐标相乘,你会得到什么?
此题涉及有序距离列表的遍历,以及集合的合并,用SQL实现有点困难,所以让DeepSeek帮助实现了,主要卡在英文的理解上,如果有两点个距离最近应该加入,但各自在不同组里了,怎么办?还有这里的10次,1000次,到底是处理了10个点,还是10个最短连接?好在经过试验,确认了以上需求是合并两个组和10个连接。以下代码通过了示例数据和正式输入数据的测试。
import math
from collections import defaultdict
def distance(p1, p2):
return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2 + (p1[2]-p2[2])**2)
def main():
with open('2508-input.txt', 'r') as f:
lines = f.read().strip().splitlines()
points = []
for line in lines:
x, y, z = map(int, line.strip().split(','))
points.append((x, y, z))
n = len(points)
print(f"总接线盒数量: {n}")
# 生成所有边并排序
edges = []
for i in range(n):
for j in range(i+1, n):
d = distance(points[i], points[j])
edges.append((d, i, j))
edges.sort(key=lambda x: x[0])
print(f"前5条最短边:")
for i, (d, a, b) in enumerate(edges[:10]):
print(f" {i+1}. 点{a}↔点{b} 距离={d:.2f}")
# 初始化:每个点在自己的电路中
circuits = [] # 每个电路是一个点集合
point_to_circuit = {} # 点到电路的映射
for i in range(n):
circuits.append({i})
point_to_circuit[i] = i
print(f"\n初始状态: 每个点在自己的电路中")
for i in range(min(5, n)):
print(f" 电路{i}: 点{i}")
connections_made = 0
connections_to_show = 10 # 显示前10次连接
for d, a, b in edges:
if connections_made >= 1000:
break
connections_made += 1
# 查找点a和点b所在的电路
circuit_a = point_to_circuit[a]
circuit_b = point_to_circuit[b]
if connections_made <= connections_to_show:
print(f"\n{'='*60}")
print(f"连接 #{connections_made}: 点{a}{points[a]} ↔ 点{b}{points[b]} 距离={d:.2f}")
if circuit_a == circuit_b:
# 两点已在同一电路,无需操作
if connections_made <= connections_to_show:
print(f" 点{a}和点{b}已在同一电路{circuit_a},跳过连接")
continue
# 获取电路大小
size_a = len(circuits[circuit_a])
size_b = len(circuits[circuit_b])
# 情况1:一个点是单点,另一个是多点电路 - 单点加入多点电路
if size_a == 1 and size_b > 1:
# 点a是单点,点b在多点电路中,把a加入b的电路
circuits[circuit_b].add(a)
point_to_circuit[a] = circuit_b
circuits[circuit_a] = set() # 清空单点电路
if connections_made <= connections_to_show:
print(f" 点{a}(单点)加入电路{circuit_b} (点{b}在其中)")
print(f" 电路{circuit_b}现在包含: {sorted(circuits[circuit_b])}")
print(f" 电路{circuit_a}现在为空")
elif size_b == 1 and size_a > 1:
# 点b是单点,点a在多点电路中,把b加入a的电路
circuits[circuit_a].add(b)
point_to_circuit[b] = circuit_a
circuits[circuit_b] = set() # 清空单点电路
if connections_made <= connections_to_show:
print(f" 点{b}(单点)加入电路{circuit_a} (点{a}在其中)")
print(f" 电路{circuit_a}现在包含: {sorted(circuits[circuit_a])}")
print(f" 电路{circuit_b}现在为空")
elif size_a == 1 and size_b == 1:
# 两个都是单点电路,合并为新电路
# 复用编号较小的电路
if circuit_a < circuit_b:
# 把点b从电路b移到电路a
circuits[circuit_a].add(b)
point_to_circuit[b] = circuit_a
circuits[circuit_b] = set() # 清空电路b
target_circuit = circuit_a
else:
# 把点a从电路a移到电路b
circuits[circuit_b].add(a)
point_to_circuit[a] = circuit_b
circuits[circuit_a] = set() # 清空电路a
target_circuit = circuit_b
if connections_made <= connections_to_show:
print(f" 两个单点电路合并到电路{target_circuit}")
print(f" 电路{target_circuit}现在包含: 点{a}, 点{b}")
else:
# 两个点都在多点电路中,合并两个电路
# 保留编号较小的电路
if circuit_a < circuit_b:
target_circuit = circuit_a
source_circuit = circuit_b
else:
target_circuit = circuit_b
source_circuit = circuit_a
# 把源电路的所有点移到目标电路
for point in circuits[source_circuit]:
point_to_circuit[point] = target_circuit
circuits[target_circuit].update(circuits[source_circuit])
circuits[source_circuit] = set() # 清空源电路
if connections_made <= connections_to_show:
print(f" 两个多点电路合并: 电路{source_circuit}合并到电路{target_circuit}")
print(f" 电路{target_circuit}现在包含: {sorted(circuits[target_circuit])}")
print(f" 电路{source_circuit}现在为空")
# 完成1000次连接后的状态
print(f"\n{'='*60}")
print(f"完成{connections_made}次有效连接后的状态:")
# 收集非空电路
non_empty_circuits = []
for i, circuit in enumerate(circuits):
if circuit:
non_empty_circuits.append((i, circuit))
print(f"非空电路数量: {len(non_empty_circuits)}")
# 按电路大小排序
circuit_sizes = []
for i, circuit in non_empty_circuits:
size = len(circuit)
circuit_sizes.append(size)
if len(circuit_sizes) <= 10: # 显示前10个电路
print(f" 电路{i}: {size}个点 - {sorted(circuit)}")
circuit_sizes.sort(reverse=True)
print(f"\n所有电路大小 (降序): {circuit_sizes}")
if len(circuit_sizes) >= 3:
result = circuit_sizes[0] * circuit_sizes[1] * circuit_sizes[2]
print(f"三个最大电路大小: {circuit_sizes[0]}, {circuit_sizes[1]}, {circuit_sizes[2]}")
print(f"乘积结果: {result}")
else:
result = 0
print("电路数量不足3个")
return result
if __name__ == "__main__":
main()
事后发现,距离最近两点不在一个电路的情况处理逻辑可统一为,合并两个电路,保留编号较小的,删除编号大的电路,也就是说,跳过获取电路大小以及三种情况的分支,直接else处理, 经检验,结果完全一致。可以减少46行代码。
第二部分很简单,添加一个腾空电路集合次数的指示变量,然后在它等于接线盒总个数减一时,将这当前最短连接的两个接线盒的 X 坐标相乘输出,并退出循环即可。
...
circuits[source_circuit] = set() # 清空源电路
empty_circuits_no+=1
if connections_made <= connections_to_show:
print(f" 两个多点电路合并: 电路{source_circuit}合并到电路{target_circuit}")
print(f" 电路{target_circuit}现在包含: {sorted(circuits[target_circuit])}")
print(f" 电路{source_circuit}现在为空")
if empty_circuits_no>= n-1:
print(a, b, points[a], points[b])
break

510

被折叠的 条评论
为什么被折叠?



