グラフ構造において強連結を求めて処理することは多いけど、 蟻本に載っているタイプのScc*1だと後の処理が結構使い辛い気がしてた。 単にSccするだけならば別に良いんだけど、辺に対して操作をする時、 その辺は強連結をするための辺なのか強連結成分同士を繋ぐ辺なのかの判定が面倒だったり、 橋だけほしいのに実装の重いSccを書いてから求めるとか、結構だるい(自己基準)。
グラフにおける橋
グラフ内で削除すると到達不可能な頂点が出る辺を橋といって、 求めるのに方法は色々とあるんだけど意外と書いてない。 ただ、日本語の記事だといい感じに説明されたものもあるので、 アルゴリズムの詳細は以下の記事を参考にしてほしい。
橋(bridge)検出アルゴリズム - nupiocaの日記
実装
上記記事の実装だと、from, toのペアが帰ってきていて、辺の判断が辛そうであったので 橋の情報の持ち方を変更して行列に置換した。 基本的に(個人の)Sccのライブラリと共存できそうな構造体を定義して、いい感じに。 アルゴリズムの変更点は無い。
/* 0-origin */ template<int V> struct Bridge { int low[V], pre[V], cnt; vector<int> edge[V]; bool res[V][V]; void init() { cnt = 0; memset(low, 0, sizeof(low)); memset(pre, 0, sizeof(pre)); memset(res, false, sizeof(res)); for (int i=0; i<V; i++) edge[i].clear(); } void add_edge(int from, int to) { edge[from].push_back(to); } void add_edge_multi(int from, int to) { add_edge(from, to); add_edge(to, from); } int dfs(int cur, int from) { low[cur] = pre[cur] = ++cnt; for (int i=0; i<edge[cur].size(); i++) { int to = edge[cur][i]; if (!pre[to]) { low[cur] = min(low[cur], dfs(to, cur)); if (pre[to] == low[to]) { res[cur][to] = true; } } else if (from != to) { low[cur] = min(low[cur], low[to]); } } return low[cur]; } void build(int n) { for (int i=0; i<n; i++) { if (!pre[i]) dfs(i, i); } } };
*1:強連結成分分解, Stronglu Connected Components