最大流を解くアルゴリズムの一つに, Dinic 法というヤツがあります.
Dinic 法については, とりあえず, 蟻本のコラム(p. 194)に書いてある事を把握しましょう.
ここでは, "1ステップ" というのは, 一度 bfs をしてから, もう一度 bfs をするまでを指す事にします.
各ステップの dfs が で終わる事は, そんなに自明ではないです.
実際,
なので, 単純に掛け算すると になります.
参考文献1を眺めると, になる事を見るには, 辺が使われなくなるタイミングに注目すればよいようです.
注意2までを読んでからの方が解りやすい気がしますが, 大雑把言うと, 多分こういう感じです.
元々は, と 両方から bfs をして, 真に関係ある頂点と辺だけを取り出します.
フローを流すときも, それによって使えなくなった辺や頂点はその場で取り除くようにしています.
普段よく使っているのは, どうやら Cherkassky version に近いようです.(参考文献1)
bfs では, からの距離で, 各頂点をラベリングしています.
dfs で見るのは, 始点のラベルが終点のラベルより小さい辺のみです.
この辺のみを見ると, 例えば次のようなグラフになります.
いくつかの注意を, このグラフ上で, から に だけ流れ, このステップが終わるまでに, dfs がどう動くかと共に見ていきます.
蟻本(p. 194) に書いてある
無駄な辺を何度も調べない
というのは, dfs内のループが iter[v] をインデックスとして回っている事に対応しています.
これはかなり重要で, これがないと, 入力によってはかなり遅くなります.
Dinic 法のライブラリを持っている人は, ちゃんとそうなっているか確かめましょう. (そうしていない人もそれなりに多いようです.)
これを怠った場合, dfs は次の順序で頂点を見る事になります.
一回目の dfs では,
により,
なる増加パスを見つけ, これに沿ってに 1 流します.
二回目の dfs では,
により,
なる増加パスを見つけ, これに沿ってに 1 流します.
次の dfs は失敗し, これ以上 から へは流れない事が解り, このステップは終了します.
一方, ちゃんと実装した場合, 一回目の dfs は同じですが, 二回目の dfs では,
により,
なる増加パスを見つけます.
前回の dfs の続きから始めるというイメージです.
これをしないと, オーダーレベルで遅くなるはず.
一回目の dfs で
なる増加パスを見つけた時, 使えなくなる辺は のみです.
従って, 次の dfs でも, まで探索される事がわかり, ここから継続して dfs を行うようにすればよい事が解ります.
少し工夫すれば, 各ステップの dfs が一回で済むように実装する事が出来, この時の探索順序は
のようになります.
上の探索順を見ていると, 明らかに に辿りつけない を探索するのは無駄だなぁという感じがします.
これを解消するには, dfs を から, 逆グラフに沿ってするように変更すればよいです.
この時の探索順序は,
となります.
bfs と dfs を逆から探索するようにすればよいので, からの距離でラベリングし, から dfs を行なってもよいです.
bfs を順グラフ, dfs を逆グラフで行うほうが, 実測でよい結果が出ましたが, これは, グラフの持ち方の問題で, 逆グラフでの bfs で辺をイテレートする際のキャッシュヒット率が低い事によるものと思われます. (dfs の方はどっちにしろ低くなる.)
7742015/10/15 23:28「Dinic 法と, その注意点」の注意2の部分について
探索順序は
S, a, b, a, e, a, S, b, e, b, f, b, S, c, g, T, g, c, h, T, h, c, S
ではないですか?最初に g に来たときに c に戻るのはなぜでしょうか.
私が理解していない/勘違いしていたら済みません.
Mi_Sawa2015/10/15 23:39(最初の b が d ですが)そのとおりですね. ご指摘ありがとうございます, 修正します.