Advent Of Code 2022 第九天 - 绳索桥
本文是关于 Advent Of Code 2022 的。
编程圣诞日历,圣诞节前每天一个编程问题。
有兴趣的朋友可以在官网答题,用自己喜欢的编程方式去收集星星⭐️。在官网答题需要登陆并下载为你单独生成的谜题输入,每个人的都不一样。
问题
在头移动的时候移动绳子的绳段。
第一部分
当你在桥上行走时,这座绳索桥吱吱作响。你不确定它有多老,或者它是否能支撑你的体重。
不过,它似乎可以支撑精灵们的体重。这座桥横跨一个峡谷,这个峡谷是由远在你下面的大河开凿出来的。
你小心翼翼地走着;当你这样做时,绳索就会伸展和扭曲。你决定通过建立绳索物理模型来分散自己的注意力;也许你甚至可以想出 不 走上去的地方。
考虑到一根绳子的两端都有一个结;这些结标志着绳子的 头 和 尾 。如果头部离尾部足够远,尾部就会被拉向头部。
由于涉及普朗克长度的模糊推理,你应该能够在一个二维网格上建立结的位置模型。然后,通过对头部进行一系列 假设的运动 (你的谜题输入),你可以确定尾部将如何移动。
由于前面提到的普朗克长度,绳子必须相当短;事实上,头部(H
)和尾部(T
)必须 一直接触 (对角线相邻,甚至重叠都算作接触):
1 | .... |
如果头离尾巴直接上、下、左、右两步,尾巴也必须朝这个方向移动一步,以便保持足够的距离:
1 | ..... ..... ..... |
否则,如果头和尾没有接触,而且不在同一行或同一列,尾巴总是斜着走一步来跟上:
1 | ..... ..... ..... |
你只需要计算出在头部做一系列动作时,尾巴的去向。假设头部和尾部都从同一位置开始,重叠在一起。
比如说:
1 | R 4 |
这一系列的动作使头部 向右 四步,然后 向上 四步,然后 向左 三步,然后 向下 一步,如此反复。每一步之后,如果这一步意味着头部不再与尾部相邻,你就需要更新尾部的位置。从视觉上看,这些运动发生如下(s
标志着作为参考点的起始位置)。
可视化
1 | == 初始态 == |
模拟绳索后,你可以计算出 尾巴至少访问过一次的所有位置 。在这个图中,s
再次标记了起始位置(尾巴也访问了这个位置),#
标记了尾巴访问的其他位置:
1 | ..##.. |
因此,有 13
个位置的尾巴至少去过一次。
模拟你完整的运动。 绳子的尾巴至少去过多少个位置?
第二部分
绳索断裂了! 突然间,河水变得比你记忆中的要近得多。桥还在那里,但一些断裂的绳索现在正向你呼啸而来,你在空中坠落!这时,你必须抓住绳索。
绳索的移动速度太快,你无法抓住;你只有几秒钟的时间来选择如何拱起你的身体以避免被击中。幸运的是,你的模拟可以扩展到支持更长的绳索。
你现在必须模拟由 十个 结组成的绳子,而不是两个结。一个结仍然是绳子的头,并根据一系列的动作来移动。再往下的每一个绳结都按照之前的规则跟随前面的绳结。
使用与上例相同的运动系列,但标有 H
, 1
, 2
, …, 9
的绳结,现在的运动情况如下。
可视化
1 | == 初始态 == |
现在,你需要跟踪新的尾巴9
访问的位置。在这个例子中,尾巴从不移动,所以它只访问 1
的位置。然而, 要小心 比以前可能有更多的运动类型,所以你可能想把你模拟的绳子和上面的绳子进行视觉上的比较。
这里有一个更大的例子。
1 | R 5 |
这些运动发生如下(个别步骤没有显示):
可视化
1 | == 初始态 == |
现在,尾巴(9
)访问了 36
个位置(包括s
)至少一次。
1 | .......................... |
在一根有10个结的大绳上模拟你的一系列动作。 绳子的尾巴至少到过多少个位置?
代码
完整的代码可以在 这里 找到。
1 | use std::cmp::{max, min}; |
解析器
将输入变成方向的 enum
和次数。
第一部分
每一次运动首先现将头往方向移动,然后判断之后的绳结是否超出了切比雪夫距离 1,这代表那个绳结需要移动。
如果需要移动就把绳结往头的方向移动最多 1 距离。
如果移动的绳结是最后一个,就把位置记录到一个 Grid
里面。
第二部分
和第一部分一样,只不过绳结长度比较长。
运行时间
所有时间由我这垃圾笔记本电脑进行(不科学的)测试,均以 --release
启动。
1 | Day 9 |