原题地址 https://adventofcode.com/2022/day/8
第二部分
精灵们对现有的树木覆盖量感到满意,现在他们只需要知道搭建树屋的最佳位置:他们希望能够看到很多树。
要测量从给定树出发的观赏距离,请从该树向上、下、左、右四个方向看;如果你到达了边缘,或者遇到了第一棵高度等于或高于所考虑树木的树,就停止计数。(如果一棵树正好在边缘,那么它至少有一个方向的观赏距离为零。)
精灵们不关心比上述规则找到的树更远的更高树木;提议的树屋有宽大的屋檐以保持干燥,反正他们也看不到比树屋更高的地方。
在上面的例子中,考虑第二行中间的那个 5:
30373
25512
65332
33549
35390
- 向上看,视线未被阻挡;可以看到 1 棵树(高度为 3)。
- 向左看,视线立即被阻挡;只能看到 1 棵树(高度为 5,紧挨着它)。
- 向右看,视线未被阻挡;可以看到 2 棵树。
- 向下看,视线最终被阻挡;可以看到 2 棵树(一棵高度为 3,然后是阻挡视线的高度为 5 的树)。
一棵树的景观得分是通过将其四个方向的观赏距离相乘得到的。对于这棵树,得分是 4(由 1 * 1 * 2 * 2 得出)。
然而,你可以找到更好的位置:考虑第四行中间那个高度为 5 的树:
30373
25512
65332
33549
35390
- 向上看,视线在 2 棵树后被阻挡(被另一棵高度为 5 的树阻挡)。
- 向左看,视线未被阻挡;可以看到 2 棵树。
- 向下看,视线也未被阻挡;可以看到 1 棵树。
- 向右看,视线在 2 棵树后被阻挡(被一棵高度为 9 的巨大树阻挡)。
这棵树的景观得分是 8(2 * 2 * 1 * 2);这是搭建树屋的理想位置。
考虑你地图上的每一棵树。任何一棵树可能获得的最高景观得分是多少?
我的解题思路:
第二部分与第一部分其实很像,主要区别有三个,
- 第一部分是从外往里看,第二部分是从里往外看
- 第一部分在地上看,第二部分在树屋上看, 能看到所有比它低的树,直到有和它等高或更高的树阻挡,然后就只能看更远更高的树了。
- 第一部分是从4个方向围着看整个树林,第二部分从单独一棵树向四方看
这些区别,使得解答也有区别:
- 统计每一个数后面:小于它的数的个数+从第一个大于等于它的数开始,递增的数的个数。
比如:3 3 5 4 9,第1个数3:向后没有小于它的数,第一个大于等于它的数是第二个数3,递增的有5、9,符合的数的个数是0+3=3。第2个数3:向后没有小于它的数,第一个大于等于它的数是第三个数5,递增的有9,符合的数的个数是0+2=2。第3个数5:向后小于它的数是4,第一个大于等于它的数是第五个数9,递增的没有,符合的数的个数是1+1=2。第4个数4::向后没有小于它的数,第一个大于等于它的数是第五个数9,递增的没有,符合的数的个数是0+1=1。第5个数9:后面没有数,符合的数的个数是0。 - 外围一圈的树其实没必要尝试,因为总有一个方向看不到树。
我用第一题类似的分析函数,但是不需要去重、因为一个方向不会重复,直接计数即可。计数分两种条件:累计最高的树小于所在树的数量,因为只要低于它的树总能被看见,累计最高的树大于或等于所在的数量,这就是第一部分的情况。
最终的sql如下,注意硬编码5表示正方形区域的边长,要根据实际数据修改。正式输入数据是99。
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))
,c as(select rn, (rn-1)//5 r, (rn-1)%5 c, b from b)
,four_count AS (
SELECT n1.* ,
(with m as(SELECT c.*, max(c.b)over(partition by c.r order by c.rn)maxb FROM c WHERE c.r = n1.r AND c.c > n1.c ),
n as(select maxb, maxb::int-coalesce(lag(maxb)over(partition by r order by rn)::int, -1)diff from m)
select count(case when n.maxb<n1.b then 1 end)+ count(case when n.maxb>=n1.b and diff>0 then 1 end) from n)cnt_right,
(with m as(SELECT c.*, max(c.b)over(partition by c.r order by c.rn desc)maxb FROM c WHERE c.r = n1.r AND c.c < n1.c ),
n as(select maxb, maxb::int-coalesce(lag(maxb)over(partition by r order by rn desc)::int, -1)diff from m)
select count(case when n.maxb<n1.b then 1 end)+ count(case when n.maxb>=n1.b and diff>0 then 1 end) from n)cnt_left,
(with m as(SELECT c.*, max(c.b)over(partition by c.c order by c.rn)maxb FROM c WHERE c.c = n1.c AND c.r > n1.r ),
n as(select maxb, maxb::int-coalesce(lag(maxb)over(partition by c order by rn)::int, -1)diff from m)
select count(case when n.maxb<n1.b then 1 end)+ count(case when n.maxb>=n1.b and diff>0 then 1 end) from n)cnt_down,
(with m as(SELECT c.*, max(c.b)over(partition by c.c order by c.rn desc )maxb FROM c WHERE c.c = n1.c AND c.r < n1.r ),
n as(select maxb, maxb::int-coalesce(lag(maxb)over(partition by c order by rn desc )::int, -1)diff from m)
select count(case when n.maxb<n1.b then 1 end)+ count(case when n.maxb>=n1.b and diff>0 then 1 end) from n)cnt_up,
FROM c n1 where n1.c >0 and n1.r>0
)
select max(cnt_right*cnt_left*cnt_down*cnt_up) maxpcnt from four_count;
--select rn, cnt_right*cnt_left*cnt_down*cnt_up maxpcnt from four_count order by rn;
补记:
提交答案通过后,我一度以为题目出错了,因为我的代码逻辑是一直数到最远方的高树,而不是遇到等高或更高的树,数上它就停止。通过把count修改成case when count(case when n.maxb>=n1.b and diff>0 then 1 end) >0 then 1 else 0 end或sign(count(case when n.maxb>=n1.b and diff>0 then 1 end))来只取一个,结果居然一模一样。而深入研究取到最大值的点,发现这个点实在很特殊,它在左、上、下三个方向上都不存在和它相同高度的树,如下图所示,它在左侧标蓝处(第54行15列),所以对于这个点后半段count恒为0,而向右方第一个遇到的就是比它还高的9,直接停止继续观望了,这就是碰巧结果正确的真相。通过求和而不是最大值,可以检验两种算法的结果其实是不同的。难怪原题举例用了一个景观分为8的点,而不是我算的12分的点。既然原算法不符合题意,就要改正。除了上述改法以外,还有可简化的地方,即不用考虑diff是否为0,中间那个算diff的子查询可以省略。最终改正的代码如下,简单了不少。
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))
,c as(select rn, (rn-1)//5 r, (rn-1)%5 c, b from b)
,four_count AS (
SELECT n1.* ,
(with n as(SELECT c.*, max(c.b)over(partition by c.r order by c.rn)maxb FROM c WHERE c.r = n1.r AND c.c > n1.c )
select count(case when n.maxb<n1.b then 1 end)+ sign(count(case when n.maxb>=n1.b then 1 end)) from n)cnt_right,
(with n as(SELECT c.*, max(c.b)over(partition by c.r order by c.rn desc)maxb FROM c WHERE c.r = n1.r AND c.c < n1.c )
select count(case when n.maxb<n1.b then 1 end)+ sign(count(case when n.maxb>=n1.b then 1 end)) from n)cnt_left,
(with n as(SELECT c.*, max(c.b)over(partition by c.c order by c.rn)maxb FROM c WHERE c.c = n1.c AND c.r > n1.r )
select count(case when n.maxb<n1.b then 1 end)+ sign(count(case when n.maxb>=n1.b then 1 end)) from n)cnt_down,
(with n as(SELECT c.*, max(c.b)over(partition by c.c order by c.rn desc )maxb FROM c WHERE c.c = n1.c AND c.r < n1.r )
select count(case when n.maxb<n1.b then 1 end)+ sign(count(case when n.maxb>=n1.b then 1 end)) from n)cnt_up,
FROM c n1 where n1.c >0 and n1.r>0
)
select max(cnt_right*cnt_left*cnt_down*cnt_up) maxpcnt from four_count;

1116

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



