利用Duckdb求解Advent of Code 2024第8题 共振共线

编程达人挑战赛·第5期 10w+人浏览 217人参与

原题地址

— 第8天:共振共线 —

你们发现自己身处一个顶级的复活节兔子设施屋顶上。

当历史学家们忙他们的事时,你看了看那熟悉的巨大天线。令你惊讶的是,它似乎已被重新配置,能发射一种信号,使人们在购买复活节兔子品牌仿制普通巧克力作为圣诞礼物时,购买的可能性增加0.1%!这简直难以置信!

扫描整个城市,你发现实际上有许多这样的天线。每根天线都调谐到一个特定的频率,用一个单一的小写字母、大写字母或数字表示。你绘制了这些天线的地图(你的谜题输入)。例如:

............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............

信号只在基于天线共振频率的特定反节点处施加其有害影响。具体来说,当某一点与两个频率相同的天线完美共线时,就会出现反节点——但前提是其中一个天线的距离是另一个的两倍。这意味着对于任何一对频率相同的天线,存在两个反节点,分别位于它们的两侧。

因此,对于这两个频率为 a 的天线,它们创建了两个用 # 标记的反节点:

..........
...#......
..........
....a.....
..........
.....a....
..........
......#...
..........
..........

增加第三个相同频率的天线会创建更多的反节点。理想情况下会增加四个反节点,但有两个超出了地图的右侧,因此实际上只增加了两个:

..........
...#......
#.........
....a.....
........a.
.....a....
..#.......
......#...
..........
..........

不同频率的天线不会产生反节点;Aa 被视为不同的频率。然而,反节点可能出现在已有天线存在的位置。在下图中,那个独立的大写字母 A 频率天线没有产生任何反节点,但它的位置上存在一个小写字母 a 频率的反节点:

..........
...#......
#.........
....a.....
........a.
.....a....
..#.......
......A...
..........
..........

第一个例子中有两种不同频率的天线,因此它们创建的反节点看起来像这样,加上一个与最顶端的 A 频率天线重叠的反节点:

......#....#
...#....0...
....#0....#.
..#....0....
....0....#..
.#....A.....
...#........
#......#....
........A...
.........A..
..........#.
..........#.

由于最顶端的 A 频率天线与一个 0 频率的反节点重叠,因此地图边界内共有14个不同的位置包含反节点。

计算信号的影响。地图边界内有多少个包含反节点的不同位置?

— 第二部分 —

在你工作时,一位历史学家从你肩后看着,询问你是否在计算中考虑了共振谐波的影响。

糟糕!

更新模型后,事实证明,任何与至少两个相同频率天线完全共线的网格位置都会出现反节点,无论距离如何。这意味着一些新的反节点将出现在每个天线的位置(除非该天线是其频率的唯一一个)。

因此,这三个 T 频率的天线现在会创建许多反节点:

T....#....
...T......
.T....#...
.........#
..#.......
..........
...#......
..........
....#.....
..........

实际上,这三个 T 频率的天线都与另外两个天线完全共线,因此它们本身也都是反节点!这使得上面例子中的反节点总数达到 9 个。

最初的例子现在有 34 个反节点,包括出现在每个天线上的反节点:

##....#....#
.#.#....0...
..#.#0....#.
..##...0....
....0....#..
.#...#A....#
...#..#.....
#....#.#....
..#.....A...
....#....A..
.#........#.
...#......##

使用这个更新后的模型计算信号的影响。地图边界内有多少个包含反节点的不同位置?

原题啰啰嗦嗦写了那么多,其实第一部分,就是求频率相同(即字符相同)的两点以其中一点为对称中心,另一点的对称点,也就是说对称点和原始点的线段连线中点刚好是另一个原始点,那么用中点公式就可以了。
第二部分,就是求频率相同(即字符相同)的两点所在的直线与坐标系中的格点(也就是图中标出位置的各点)的重合点。

那么程序就很简单了
第一部分

with t as(
select
'............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............' t)
, 
b as(select row_number()over()rn,(rn-1)//12 r, (rn-1)%12 c,b from(select unnest(string_split(replace(t,chr(10), ''), ''))b from t))
,c as(select * from b where b<>'.')
,p1 as(select 2*c1.c-c2.c nc, 2*c1.r-c2.r nr, 1+nc+nr*12 nrn from c c1, c c2 where c1.rn<c2.rn and c1.b=c2.b and nc between 0 and 12-1 and nr between 0 and 12-1)
,p2 as(select 2*c2.c-c1.c nc, 2*c2.r-c1.r nr, 1+nc+nr*12 nrn from c c1, c c2 where c1.rn<c2.rn and c1.b=c2.b and nc between 0 and 12-1 and nr between 0 and 12-1)
,ap as(select nrn from p1 union select nrn from p2)
select count(*)from ap;

,mp as(select listagg(case when ap.nrn is not null then '#' else b.b end , '' order by b.rn)t from b left join ap on b.rn=ap.nrn)
select i, substr(t, 1+i*12, 12) from mp, range(12)t(i) order by i;

其中p1是靠前的对称点,p2是靠后的对称点,用union去重。mp是将算出的反节点回填到原图中,再分行输出。硬编码12是行列数。
第二部分, 其中乘法等式是两点式直线方程,不用除法是为了避免斜率不存在、分母为0的情况。count用distinct去重。

with t as(
select
'............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............' t)
, 
b as(select row_number()over()rn,(rn-1)//12 r, (rn-1)%12 c,b from(select unnest(string_split(replace(t,chr(10), ''), ''))b from t))
,c as(select * from b where b<>'.')

select count(distinct b.rn) from b, c c1, c c2 where c1.rn<c2.rn and c1.b=c2.b and (b.r-c1.r)*(b.c-c2.c)=(b.r-c2.r)*(b.c-c1.c)
;
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值