利用Duckdb求解Advent of Code 2022第8题 树顶木屋 第一部分

用DuckDB解AoC2022第8题

原题地址 https://adventofcode.com/2022/day/8

第 8 天:树顶木屋

探险队遇到了一片奇特的高大树林,所有树木都被精心地种植在网格中。精灵们解释说,这是之前一次探险队为重新造林而种植的这些树。现在,他们想知道这是否是一个搭建树屋的好地方。

首先,要确定这里的树木覆盖是否足够将树屋隐藏起来。为此,你需要计算当沿着行或列的方向直接从网格外看时,能够看到的树木数量。

精灵们已经放飞了一架四轴飞行器来生成一张包含每棵树高度的地图(你的谜题输入)。例如:

30373
25512
65332
33549
35390

每棵树用一个数字表示其高度,其中 0 是最矮的,9 是最高的。

如果一棵树与网格边缘之间的所有其他树都比它矮,那么这棵树就是可见的。只考虑同一行或同一列上的树;也就是说,只从给定的树向上、下、左或右看。

网格边缘的所有树都是可见的——因为它们已经在边缘,没有树木会阻挡视线。在这个例子中,只剩下内部的九棵树需要考虑:

  • 左上角的 5 从左边和顶部看是可见的。(从右边和底部看不可见,因为有其他高度为 5 的树挡住了。)
  • 顶部中间的 5 从顶部和右边看是可见的。
  • 右上角的 1 从任何方向都不可见;要使它可见,需要它与边缘之间只有高度为 0 的树。
  • 左边中间的 5 是可见的,但仅从右边看是可见的。
  • 中心的 3 从任何方向都不可见;要使它可见,需要它与边缘之间只有高度最多为 2 的树。
  • 右边中间的 3 从右边看是可见的。
  • 在底行,中间的 5 是可见的,但 3 和 4 不可见。

在这个布局中,有 16 棵边缘的树可见,另外内部有 5 棵可见,总共有 21 棵树是可见的。

考虑你的地图;从网格外可以看到多少棵树?

解题思路
从网格外四个方向能看到一棵树的条件是:这棵树在当前方向上到目前为止是最高的,如果有等高的,只有第一棵达到这个高度的树能被看到,
以第一行向右看为例,第一个3能看到,因为在边缘,第二个0看不到,被第一棵挡住,第三个3看不到,与第一个3等高,也被挡住,第4个7看得到,它是当前方向目前最高的。第5个3看不到,被第4个7挡住。
对于这种累计问题,很容易想到用max分析函数解决, 为了解决等高问题,在max结果的基础上,用lag分析函数求当前与前一个的差,等于0的为等高,大于0的是符合条件的树。对于边缘,没有前一个,lag返回NULL, 因为树高度有为0的,需要将前值特殊处理成-1,以便统一使差值大于0。
除了行方向,还有列方向,根据树的总编号确定行列,分别按行分区,同样的行按列排序,包括正序和逆序。
最后的SQL如下

with t as(select '
30373
25512
65332
33549
35390' t), 
b as(select row_number()over()rn,b from(select unnest(string_split(replace(t,chr(10), ''), ''))b from t))
--from b limit 13;
,c as(select rn, (rn-1)//5 r, (rn-1)%5 c, b,
max(b) over(partition by c  order by rn)maxb_ca,       --top
max(b) over(partition by c  order by rn desc)maxb_cd,  --bot
max(b) over(partition by r  order by rn)maxb_ra,       --left 
max(b) over(partition by r  order by rn desc)maxb_rd   --right
from b)
--from c;
,m as(select rn, r, c, b, 
maxb_ca, maxb_ca::int-coalesce(lag(maxb_ca)over(partition by c  order by rn)::int, -1)diff1, 
maxb_cd, maxb_cd::int-coalesce(lag(maxb_cd)over(partition by c  order by rn desc)::int, -1)diff2, 
maxb_ra, maxb_ra::int-coalesce(lag(maxb_ra)over(partition by r  order by rn)::int, -1)diff3, 
maxb_rd, maxb_rd::int-coalesce(lag(maxb_rd)over(partition by r  order by rn desc)::int, -1)diff4, 
from c)
--from m order by  r, c desc;
,fourside as( 
select rn from m where diff1>0
union
select rn from m where diff2>0
union
select rn from m where diff3>0
union
select rn from m where diff4>0
) 
select count(*)from fourside;

这里的硬编码5是每行的列数,要根据具体实际数据调整,题目正式输入文件是99列。
执行结果如下, 包括中间输出结果和最后从四个方向能看见的树总数。注意要排除重复的树,这里用union语句达成。从表格前5行maxb_ra列可见,第一行从左到右看见的树是rn=1和4的树,和刚才的分析一致。而从右到左要从maxb_rd列第5行往第一行看,分别是rn=5和4,如果要看各列的,按先列后行的顺序排序后更直观。

memory D .read 2208code.txt
┌───────┬───────┬───────┬─────────┬─────────┬───────┬─────────┬───────┬─────────┬───────┬─────────┬───────┐
│  rn   │   r   │   c   │    b    │ maxb_ca │ diff1 │ maxb_cd │ diff2 │ maxb_ra │ diff3 │ maxb_rd │ diff4 │
│ int64 │ int64 │ int64 │ varcharvarchar │ int32 │ varchar │ int32 │ varchar │ int32 │ varchar │ int32 │
├───────┼───────┼───────┼─────────┼─────────┼───────┼─────────┼───────┼─────────┼───────┼─────────┼───────┤
│     100334603470 │
│     201001503070 │
│     302334503070 │
│     403778907474 │
│     504334907034 │
│     610230602350 │
│     711555505350 │
│     812552505053 │
│     913170905020 │
│    1014230905023 │
│    1120663636761 │
│    1221550506052 │
│    1322350506030 │
│    1423370906031 │
│    1524230906023 │
│    1630360303490 │
│    1731350503090 │
│    1832550525290 │
│    1933470905090 │
│    20349969994910 │
│    2140360343490 │
│    2241550565290 │
│    2342350345090 │
│    24439929109499 │
│    2544090019001 │
├───────┴───────┴───────┴─────────┴─────────┴───────┴─────────┴───────┴─────────┴───────┴─────────┴───────┤
│ 25 rows                                                                                      12 columns │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘
memory D .read 2208code.txt
┌───────┬───────┬───────┬─────────┬─────────┬───────┬─────────┬───────┬─────────┬───────┬─────────┬───────┐
│  rn   │   r   │   c   │    b    │ maxb_ca │ diff1 │ maxb_cd │ diff2 │ maxb_ra │ diff3 │ maxb_rd │ diff4 │
│ int64 │ int64 │ int64 │ varcharvarchar │ int32 │ varchar │ int32 │ varchar │ int32 │ varchar │ int32 │
├───────┼───────┼───────┼─────────┼─────────┼───────┼─────────┼───────┼─────────┼───────┼─────────┼───────┤
│     100334603470 │
│     610230602350 │
│    1120663636761 │
│    1630360303490 │
│    2140360343490 │
│     201001503070 │
│     711555505350 │
│    1221550506052 │
│    1731350503090 │
│    2241550565290 │
│     302334503070 │
│     812552505053 │
│    1322350506030 │
│    1832550525290 │
│    2342350345090 │
│     403778907474 │
│     913170905020 │
│    1423370906031 │
│    1933470905090 │
│    24439929109499 │
│     504334907034 │
│    1014230905023 │
│    1524230906023 │
│    20349969994910 │
│    2544090019001 │
├───────┴───────┴───────┴─────────┴─────────┴───────┴─────────┴───────┴─────────┴───────┴─────────┴───────┤
│ 25 rows                                                                                      12 columns │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘
memory D .read 2208code.txt
┌──────────────┐
│ count_star() │
│    int64     │
├──────────────┤
│      21      │
└──────────────┘
Run Time (s): real 0.016 user 0.000000 sys 0.000000
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值