原题地址 。
— 第 4 天:印刷部门 —
你乘自动扶梯下到印刷部门。他们显然在为圣诞节做准备;到处都是大卷的纸,角落里甚至还有一台巨大的打印机(用来处理真正的大型打印任务)。
在这里装饰会很容易:他们可以制作自己的装饰品。你真正需要的是在电梯离线时能够进一步进入北极基地的方法。
"实际上,也许我们可以帮忙解决这个问题,"当你请求帮助时,一位精灵回答说。“我们很确定后墙的另一边有一个自助餐厅。如果我们能打通那堵墙,你就能继续前进。可惜我们所有的叉车都忙着搬运那些大卷的纸。”
如果你能优化叉车的工作,也许它们就能抽出时间来打通墙壁。
纸卷(@)被放置在一个大网格上;精灵们甚至有一张有用的图表(你的谜题输入)来指示所有东西的位置。
例如:
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.
叉车只能访问那些八个相邻位置中纸卷数量少于四个的纸卷。如果你能找出叉车可以访问哪些纸卷,它们就能花更少的时间寻找,花更多的时间拆通往自助餐厅的墙。
在这个例子中,有 13 个纸卷可以被叉车访问(用 x 标记):
..xx.xx@x.
x@@.@.@.@@
@@@@@.x.@@
@.@@@@..@.
x@.@@@@.@x
.@@@@@@@.@
.@.@.@.@@@
x.@@@.@@@@
.@@@@@@@@.
x.x.@@@.x.
考虑你的完整纸卷位置图。有多少纸卷可以被叉车访问?
此谜题的第一部分已完成!它提供了一颗金星:*
— 第二部分 —
现在,精灵们需要帮助来尽可能多地访问纸卷。
一旦一个纸卷可以被叉车访问,它就可以被移除。一旦一个纸卷被移除,叉车可能就能够访问更多的纸卷,这些纸卷也可能被移除。如果精灵们不断重复这个过程,总共可以移除多少个纸卷?
从上面相同的例子开始,以下是一种尽可能多地移除纸卷的方法,使用高亮的 @ 表示一个纸卷即将被移除,使用 x 表示一个纸卷刚刚被移除:
初始状态:
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.
移除 13 个纸卷:
..xx.xx@x.
x@@.@.@.@@
@@@@@.x.@@
@.@@@@..@.
x@.@@@@.@x
.@@@@@@@.@
.@.@.@.@@@
x.@@@.@@@@
.@@@@@@@@.
x.x.@@@.x.
移除 12 个纸卷:
.......x..
.@@.x.x.@x
x@@@@...@@
x.@@@@..x.
.@.@@@@.x.
.x@@@@@@.x
.x.@.@.@@@
..@@@.@@@@
.x@@@@@@@.
....@@@...
移除 7 个纸卷:
..........
.x@.....x.
.@@@@...xx
..@@@@....
.x.@@@@...
..@@@@@@..
...@.@.@@x
..@@@.@@@@
..x@@@@@@.
....@@@...
移除 5 个纸卷:
..........
..x.......
.x@@@.....
..@@@@....
...@@@@...
..x@@@@@..
...@.@.@@.
..x@@.@@@x
...@@@@@@.
....@@@...
移除 2 个纸卷:
..........
..........
..x@@.....
..@@@@....
...@@@@...
...@@@@@..
...@.@.@@.
...@@.@@@.
...@@@@@x.
....@@@...
移除 1 个纸卷:
..........
..........
...@@.....
..x@@@....
...@@@@...
...@@@@@..
...@.@.@@.
...@@.@@@.
...@@@@@..
....@@@...
移除 1 个纸卷:
..........
..........
...x@.....
...@@@....
...@@@@...
...@@@@@..
...@.@.@@.
...@@.@@@.
...@@@@@..
....@@@...
移除 1 个纸卷:
..........
..........
....x.....
...@@@....
...@@@@...
...@@@@@..
...@.@.@@.
...@@.@@@.
...@@@@@..
....@@@...
移除 1 个纸卷:
..........
..........
..........
...x@@....
...@@@@...
...@@@@@..
...@.@.@@.
...@@.@@@.
...@@@@@..
....@@@...
一旦没有更多的纸卷可以被叉车访问就停止。在这个例子中,总共可以移除 43 个纸卷。
从你的原始图表开始。精灵们和他们的叉车总共可以移除多少个纸卷?
解题方法:
找周围的八个方向有个计算公式,就是行列号差的绝对值小于或等于1,然后扣除它本身,统计每个卷纸附近的卷纸小于4个,就变成count小于5。
第一问的SQL如下:
with t as(select '
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.' t),
b as(select row_number()over()rn,(rn-1)//10+1 r, (rn-1)%10+1 c,b from(select unnest(string_split(replace(t,chr(10), ''), ''))b from t))
,c as(select b.rn from b,b b2 where b.b='@' and abs(b.r-b2.r)<=1 and abs(b.c-b2.c)<=1 group by b.rn having count(case when b2.b='@' then 1 end) <5)
select count(*) from c;
第二问思路与第一问完全一样,只是迭代一次就去掉一些卷纸,但是用传统的递归CTE遇到一些麻烦,下面用DuckDB专用的using key和recurring语法将被移除的纸卷做上标记。
with recursive t as(select '
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.' t),
b as(select row_number()over()rn,(rn-1)//10+1 r, (rn-1)%10+1 c,b from(select unnest(string_split(replace(t,chr(10), ''), ''))b from t))
,c using key(rn)as
(
select b.rn,b.r,b.c,b.b from b
union
select b.rn,b.r,b.c,'x' from recurring.c b,recurring.c b2 where b.b='@' and abs(b.r-b2.r)<=1 and abs(b.c-b2.c)<=1 group by b.rn,b.r,b.c,b.b having count(case when b2.b='@' then 1 end) <5
)
select count(*)from c where b='x';
如果用传统递归CTE方法,要注意每轮要检查有没有符合移除条件的行,保留那些不可被移除的。不存在可移除的纸卷时停止迭代,否则会无限循环,因为总存在不可移除的纸卷。写起来冗余一些。
with recursive t as(select '
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.' t),
b as(select row_number()over()rn,(rn-1)//10+1 r, (rn-1)%10+1 c,b from(select unnest(string_split(replace(t,chr(10), ''), ''))b from t))
,c as
(
select 1 lv,b.rn,b.r,b.c,b.b from b
union all
select * from(
select 1+any_value(b.lv),b.rn,b.r,b.c,b.b from c b,c b2 where b.b='@' and abs(b.r-b2.r)<=1 and abs(b.c-b2.c)<=1 group by b.rn,b.r,b.c,b.b having count(case when b2.b='@' then 1 end) >=5
)where exists (select b.rn,b.r,b.c,'x' from c b,c b2 where b.b='@' and abs(b.r-b2.r)<=1 and abs(b.c-b2.c)<=1 group by b.rn,b.r,b.c,b.b having count(case when b2.b='@' then 1 end) <5)
)
select count(case when b='@' and lv=1 then 1 end)-count(case when b='@' and lv=(select max(lv) from c)then 1 end) mov from c;
如果不检查,那么要手工控制迭代次数,比如b.lv <100,因为不知道几轮能移除完那些最终可移除的纸卷,只能放一个较大的值,而这样效率低很多,放小了又有移除不完的风险,所以不推荐。
with recursive t as(select '
..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.' t),
b as(select row_number()over()rn,(rn-1)//10+1 r, (rn-1)%10+1 c,b from(select unnest(string_split(replace(t,chr(10), ''), ''))b from t))
,c as
(
select 1 lv,b.rn,b.r,b.c,b.b from b
union all
select 1+any_value(b.lv),b.rn,b.r,b.c,b.b from c b,c b2 where b.b='@' and abs(b.r-b2.r)<=1 and abs(b.c-b2.c)<=1 and b.lv <100 group by b.rn,b.r,b.c,b.b having count(case when b2.b='@' then 1 end) >=5
)
select count(case when b='@' and lv=1 then 1 end)-count(case when b='@' and lv=(select max(lv) from c)then 1 end) mov from c;

681

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



