目次
パナソニックグループ プログラミングコンテスト2025(AtCoder Beginner Contest 427)E,F問題メモ
E - Wind Cleaning
問題文
- $H$ 行 $W$ 列のグリッドがあります。このグリッドの上から $i$ 行目、左から $j$ 列目のマスを $(i, j)$ と表記します。
- 高橋君はこのグリッドの $1$ つのマスの上におり、グリッド上のいくつかのマスにはゴミが落ちています。
- 各マスの状態は $H$ 個の長さ $W$ の文字列 $S_1, S_2, \ldots, S_H$ によって与えられ、$S_i$ の $j$ 文字目を $S_{i, j}$ として、以下のような状態を表します。
- $S_{i, j} =$
'T
' のときマス $(i, j)$ は高橋君がいるマスである。 - $S_{i, j} =$
'#
' のときマス $(i, j)$ はゴミが落ちているマスである。 - $S_{i, j} =$
'.
' のときマス $(i, j)$ はゴミが落ちていない空きマスである。
- また、高橋君がいるマスにはゴミは落ちていません。
- 高橋君は、以下の操作を繰り返し行うことができます。
- 上下左右のいずれかの方向を $1$ つ選び、すべてのゴミを同時にその方向へ $1$ マス移動させる。ただし、ゴミがグリッドの外に出た場合にはゴミは消滅し、高橋君のいるマスにゴミが移動した場合は高橋君は汚れる。
- 高橋君が汚れることなくすべてのゴミを消滅させることができるか判定し、できる場合は操作を行う回数の最小値を求めてください。
制約
- $2 \leq H, W \leq 12$
- $S_i$ は長さ $W$ の
'T
','#
','.
' からなる文字列 - $S_{i, j} =$
'T
' なる $(i, j)$ がちょうど $1$ つ存在する - $S_{i, j} =$
'#
' なる $(i, j)$ が$1$ つ以上存在する
解法
操作毎に「4方向のどちらにゴミを飛ばすか」の幅優先探索で解けるが、
問題設定の通りに全てのゴミを移動させ、その盤面をキーとしていると、状態が持たせづらい。
(持たせられないわけではないし、キーの更新・辞書からのキーの照合という定数倍を除けば、直に盤面を持っても計算量は変わらず解ける。
実装のしやすさからいうと、盤面持たせた方が楽だったかも)
「高橋君の方を移動させる」と考えても、相対的な高橋君とゴミの位置関係は変わらないので、 ゴミにぶつかるかどうかは管理できる。この時、高橋君は盤面の外にも移動できるとする。
ただ、「一旦ゴミを盤面から落とすと、次以降はそのゴミのマスを通過することができる」というのが曲者。
...#... ....... 一旦、上に動いて最下段のマスのゴミを落とし、 ....... 次は、下に動いて元ゴミのマスを通過しつつ、最上段のゴミを落とすのがよい。 ...T... .#####.
BFSのためには、以下がわかるような状態をキーとしなければいけない。
- 全てのゴミを落としたか?(ゴール判定)
- 移動しようとしているマスが '#' だったとき、そのゴミは残っているかいないか?
$(u,d,l,r,i,j)$ をBFSのキーとする。
- $u,d$: 高橋君が訪れたことのある最小・最大の $x$ 座標
- $l,r$: 高橋君が訪れたことのある最小・最大の $y$ 座標
- $i,j$: 高橋君の現在位置
高橋君の初期位置を $(t_x,t_y)$ とする。
- 最下段を $d$ まで移動していたとすると、$i \lt d-t_x$ にあるゴミは全て落とされている。
- 最上段を $u$ まで移動していたとすると、$i \ge H-(t_x-u)$ にあるゴミは全て落とされている。
左右についても同様に、$u,d,l,r$ の情報で落とされているゴミが分かるので、BFSの更新・ゴール判定ができる。
キーの各値は $-H~H, -W~W$ の範囲を動く(それだけ動いたら確実に全てのゴミは落とされている)。
また、ゴール判定には $O(HW)$ の計算量がかかる(二次元累積和で $O(1)$ にすることもできる)。
よって、全体で $O(H^4W^4)$ で解ける。
実際には、「上と下の両方ともに大きく動くより前に答えが見つかる」などで何分の一かになることが期待され、間に合う。
F - Not Adjacent
問題文
- 長さ $N$ の整数列 $A=(A _ 1,A _ 2,\ldots,A _ N)$ が与えられます。
- $A$ の(連続するとは限らない)部分列は $2 ^ N$ 通りあります。
- 部分列 $(A _ {i _ 1},A _ {i _ 2},\ldots,A _ {i _ k})\ (1\le i _ 1\lt i _ 2\lt\cdots\lt i _ k\le N)$ であって、次の $2$ つの条件の両方を満たすものがいくつあるか求めてください。
- 選ばれた要素は $A$ において隣接しない。つまり、すべての $1\le j\lt k$ に対して $1+i _ j\ne i _ {j+1}$ が成り立つ。
- 総和が $M$ の倍数である。つまり、$\displaystyle\sum _ {j=1} ^ kA _ {i _ j}\equiv0\pmod M$
- $2$ つの部分列が整数列として等しくても、取り出す位置が異なるならそれら $2$ つの部分列を区別して数えることに注意してください。
制約
- $1\le N \le 60$
- $1\le M \le 10 ^ 9$
- $0\le A _ i\lt M$
- 入力はすべて整数
解法
$M$ がでかいので「$\mathrm{DP}[i,d]:=A_i$ まで考慮して $\mod{M}$ が $d$ の状態の数」みたいな、 状態数が $O(M)$ 以上かかるDPはできない。
実は、ただ問題を難しくするために設定されているような1つめの条件(隣接しない)が重要で、 この条件があることで半分全列挙で解けるようになっている。
この条件が無いと、数列の半分 $N/2=30$ の取り得る部分列の個数は $2^{30} \simeq 10^9$ で多すぎるのだが、 隣接しないという条件があるとその個数はフィボナッチ数となることが知られており、 $\mathrm{fib}_{32} = 2178309$ 以下となる。 (数列の始めをどのようにするかで、A000045 - OEIS の定義より2個ずれる)
「末端要素を使うか」で分けて左右から半分ずつDPし(右半分のDPは右端から始める)、 最後に「末端を使ったもの同士」以外で合わせると、$O(\mathrm{fib}_{N/2})$ で解ける。