AdventOfCode 2022 Day 24

Part One

走迷宫,从左上入口处能够尽早地移动到右下出口处,途中需要避免相遇匀速前进的

#.######
#>>.<^<#
#.<..<<#
#>v.><>#
#<^v^^>#
######.#

是否相遇?

由于蜥蜴的移动是匀速的,因此根据初始位置,以及当前的步数,就可以推断出某一处位置是否安全。因此,函数的入参为初始位置、当前步数、判断位置,出参就是是否安全。

CREATE OR REPLACE FUNCTION is_safe(blizzards INTEGER[], step INTEGER, target INTEGER, loop INTEGER)
RETURNS BOOLEAN AS $$
BEGIN
    FOR i IN 1..(#blizzards) LOOP
      IF ((blizzards[i] + step - 2) % loop + 2) = target THEN
        return false;
      END IF;
    END LOOP;
  
    RETURN true;
   
END;
$$ LANGUAGE plpgsql;

蜥蜴的移动是循环的,因此也要给出循环的步数,即移动多少步后回到原点。

WITH recursive origin AS (
  SELECT rn as _row, x.idx as _col, x.word as pos
  FROM (
    SELECT row_number() over() as rn, line
    FROM lance_input
  ) t, regexp_split_to_table(line, '') with ordinality as x(word, idx)
), move_range AS (
  SELECT (select count(*) - 2 from origin where _col = 1) :: INTEGER as row_cnt,
         (select count(*) - 2 from origin where _row = 1) :: INTEGER as col_cnt
)

东西南北四个方向上均有蜥蜴,因此求出其四个方向上的初始条件

east_blizzard as (
  SELECT _row, array_agg(_col) :: INTEGER[] as pos_array 
  FROM origin
  WHERE pos = '>'
  GROUP BY _row
), west_blizzard as (
  SELECT _row, array_agg(_col) :: INTEGER[] as pos_array 
  FROM origin
  WHERE pos = '<'
  GROUP BY _row
), south_blizzard as (
  SELECT _col, array_agg(_row) :: INTEGER[] as pos_array 
  FROM origin
  WHERE pos = 'v'
  GROUP BY _col
), north_blizzard as (
  SELECT _col, array_agg(_row) :: INTEGER[] as pos_array 
  FROM origin
  WHERE pos = '^'
  GROUP BY _col
)

迷宫行走

每次行走,除了东西南北四个移动方向外,还可以原地不动,因此每一轮都有5个候选移动目的地

          SELECT CASE WHEN move_direction = 'N' THEN _row - 1
                      WHEN move_direction = 'S' THEN _row + 1
                      ELSE _row
                  END as _row,
                 CASE WHEN move_direction = 'W' THEN _col - 1
                      WHEN move_direction = 'E' THEN _col + 1
                      ELSE _col
                  END as _col,
                 route || move_direction as route,
                 step + 1 as step
          FROM inner_table, (SELECT regexp_split_to_table('NSWEO', '') as move_direction) s

候选目的地是否合法,判断依据就是

  • 只能移动到有效位置,包括起点、终点以及中间的非边界位置
  • 东西南北四个方向上的蜥蜴不能相遇

最终,候选位置要进行去重。因此只统计最短路径,中间具体的路径怎么走其实并不重要。终止条件,就是终于走到了最后一行。

完整行走语句

walkthrough AS (
  SELECT 1 as _row, 2 as _col, '' as route, 0 as step

  UNION ALL

  SELECT _row, _col, route, step
  FROM (
    WITH inner_table AS (
      SELECT * FROM walkthrough
    ), possible_moves AS (
      SELECT t._row, t._col, route, step,
             row_number() over (PARTITION BY t._row, t._col) as rn
      FROM (
        SELECT _row, _col, route, step
        FROM (
          SELECT CASE WHEN move_direction = 'N' THEN _row - 1
                      WHEN move_direction = 'S' THEN _row + 1
                      ELSE _row
                  END as _row,
                 CASE WHEN move_direction = 'W' THEN _col - 1
                      WHEN move_direction = 'E' THEN _col + 1
                      ELSE _col
                  END as _col,
                 route || move_direction as route,
                 step + 1 as step
          FROM inner_table, (SELECT regexp_split_to_table('NSWEO', '') as move_direction) s
          WHERE NOT EXISTS(SELECT * FROM inner_table WHERE _row = (select row_cnt + 2 from move_range))
        ) t, move_range r
        WHERE ((_row between 2 AND (r.row_cnt + 1) AND _col between 2 AND (r.col_cnt + 1))
        OR (_row = r.row_cnt + 2 AND _col = r.col_cnt + 1))
        OR (_row = 1 AND _col = 2)
      ) t
      JOIN move_range m
      ON 1 = 1
      LEFT JOIN east_blizzard e
      ON t._row = e._row
      LEFT JOIN west_blizzard w
      ON t._row = w._row
      LEFT JOIN north_blizzard n
      ON t._col = n._col
      LEFT JOIN south_blizzard s
      ON t._col = s._col
      WHERE (e._row IS NULL OR is_safe(e.pos_array, step, t._col, col_cnt))
      AND (w._row IS NULL OR is_safe(w.pos_array, col_cnt - (step % col_cnt), t._col, col_cnt))
      AND (s._col IS NULL OR is_safe(s.pos_array, step, t._row, row_cnt))
      AND (n._col IS NULL OR is_safe(n.pos_array, row_cnt - (step % row_cnt), t._row, row_cnt))
    )
    SELECT x._row, x._col, x.route, x.step
    FROM possible_moves x
    WHERE rn = 1
  ) t
)

Part Two

忘记拿东西……,需要走终点走回到起点,再回到终点。

初始条件修改即可

SELECT 1 as _row, 2 as _col, 0 as step

修改为

SELECT destination_row, destination_col, xx as step

发表评论