DDCC2020本戦-B : Hawker on Graph
こういう非自明な例が出てくると理解が進んで助かる。(間違ってたら教えてください)
問題
解法
解説の議論を追っていくことにしよう。
単に足していくだけだったら...
まず最初に、重みの辺を通るごとに所持金がだけ増え、負になることもあり得る場合を考えよう。この場合は、を「頂点から頂点に回の移動で向かうときの所持金の最大値」とすれば、遷移式は
と書ける。回目でに行くので、回目にどこにいるかで全探索していることになる。
ところで、この遷移式をよく見てみると、右辺が行列積の形をしている気がしてくる。例えばを加算、その中の加算を乗算に置き換えてみると、これは実行列の四則演算における積の計算そのものである。と加算の場合も、同様に行列を用いて計算ができる。元の問題に立ち返れば、加算はグラフ上でパスを伸ばしていく演算であり、は互いに素なパスの結果をまとめる演算ということになる。
遷移が行列で書けるということは、行列累乗による高速化を検討したくなる。行列累乗に持ち込むには、2つの演算に「結合則」(演算を行う順番を変えても結果が変わらないこと、交換則とは違う!)と、加算に交換則が成り立つこと、分配則が成り立っている必要があるが、と加算はもちろんそれが成り立っている。そういうわけで、この場合はで解くことができる。
元の問題の解法
元の問題では、所持金のときから重みの辺を通ると所持金がとなる。この変化を行列累乗に持っていくことを考えよう。そのためにまず、この所持金の変化の関数をと見て、行列の各要素にのペアを持たせることにする。次に、上述した行列積に落とすのに必要な2つの演算を考えよう。まずパスを伸ばす演算を考える。、とするととなる。ここでのときはとなり、のときはとなる。これをまとめると
となり、これはたしかに合成前の関数と同じ形をしている。
次に、パスをまとめる演算を考える。これとのということになるが、のある方を使うならとの大きい方、使わないならとの大きい方が関わるので、単にでよい。これも合成前と同じ形をしている。
今定義した2つの演算はどちらも結合則と分配則を満たし、パスをまとめる演算は交換則を満たすことがわかるので、これらの演算をもとに行列累乗が可能である。
実装
自分で演算を用意したり、今回は可換な演算ではないので2項演算でもどちらを前に置くかなどに気を付ける必要がある。おかしいなと思ったら雑に順番を入れ替えるのも手かもしれない(?)。