目次

AtCoder Beginner Contest 319 F,G問題メモ

AtCoder Beginner Contest 319

WTF(オープン)で疲れてABCもあることを完全に忘れてた。

今回から7問体制に。結局、本番中にExを通せたことはなかったね。

F - Fighter Takahashi

F - Fighter Takahashi

問題

解法

誰もプレイしたことがないのに、何故か見たことはあるというあのゲーム。
(最近は、そういう広告ゲーを集めたミニゲーム集としてリリースされてるのもあるけど)

まず、薬を飲む前に倒せる敵はできるだけ倒すのがよい。

倒す順番は関係ないので、弱い敵から倒すとしてよい。
訪問済みの頂点に隣接する頂点にいる未討伐の敵を $s_i$ をキーに優先度付きキューで管理する。

で、倒せなくなったら、隣接頂点にある薬を飲むしかない。
この順番は、敵と違い、最適解は貪欲にはなかなか決めづらい。

なので薬を飲む順番は全探索する必要がある。
ただ、本当に全探索すると $10!$ のオーダーがかかるので無理。

メモ化再帰をおこなう。

「今の強さで倒せる敵は全部倒した」状態を「薬待ち状態」とする。

飲み終えた薬の集合を $S$ とする。$S$ が同じになる薬待ち状態には、 飲んだ順番によって高橋君の強さ(とそれに伴う残り敵)は様々な状態が考えられるが、

ということが言える。

なので、再帰関数で薬待ち状態になるたびに1つ選んで飲む(再帰する)よう処理する中で、 過去に探索された飲んだ薬の集合 $S$ ごとの最大強さをメモしておき、 現在の強さがメモ以下なら、探索する必要は無い。
(全ての敵を倒す道筋が1つ見つかればよいので、探索が続いているということは、メモられた強さの場合でも倒せなかった、ということになる。現在の強さがそれ以下なら、当然倒せない)

計算量がちゃんとわかっていないので、上手いことテストケースを作れば「ちょっとずつ強さが更新されていって、探索数がかさんでしまう」というケースも作れるかもしれない。

この解法では、「現在の強さが同じ $S$ の中での最大強さであることが確定していなくても、とりあえず最後まで探索してしまう」という実装なので危ないが、本当は、公式Editorialのように「$dp[S]$ ごとに最大強さを確定させていく。最大の時の隣接敵情報も残しておく」という実装だと、間に合うことが保証される。

Python3

G - Counting Shortest Paths

G - Counting Shortest Paths

問題

解法

完全グラフの辺数は $O(N^2)$ のオーダーであるが、 削除できる辺数が限られているので、$N$ がでかい場合は最短距離はそこまで大きくできない。

具体的には、最短距離が $d$ の場合、最短パスを1つだけ取って考えても、 2つ以上離れた頂点間は辺を削除しないといけない。
少なくとも $\dfrac{d(d-1)}{2}$ の辺は削除されないといけない。

よって、だいたい $d \le \sqrt{2M}$ となる。
最短パスが複数あったり、$N$ が大きくて最短パスに含まれない頂点への辺を切断する必要があったりすると、さらに $d$ は小さくなる。

いま、頂点1からの最短距離が $k$ である頂点群 $S_k$ と、$S_k$ 内の頂点それぞれへの最短パス数 $P_v$ がわかっているとする。

ここから、最短距離が $k+1$ の頂点群 $S_{k+1}$ と最短パス数を求めたい。
これを最大 $d$ 回繰り返すと、答えが見つかるか、到達できないことが確定する。

普通の経路数数え上げのようにBFS的なことをすると、1頂点から出ている辺数が多すぎるのでTLE。
基本的に到達可能として、不可能なものは減算する、という逆のアプローチを取る。

$O((N+M)\sqrt{M})$ でとおる。

通す上では不要だが、$N$ の大小により場合分けして、小さい場合は普通にBFSによる経路数数え上げをすると高速になる。

Python3