橋検出(Bridge-finding)

概要

グラフ\(G = (V, E)\)において橋(除去するとグラフが連結でなくなる辺)を検出する。

計算量

\(O(|V| + |E|)\)

実装

橋検出して縮約したグラフを返す実装。 多重辺にも対応できるようにしている。

# Bridge-Finding
# 橋、二重辺連結成分分解
# g: 隣接リスト, v: 頂点数, s: 探索開始地点
#
# DFS木を求めながら橋を見つけ、グラフを縮約していく
def bridge_finding(G, N, start=0):
    fin = [False] * N
    v_cnts = [0] * N
    used = [False] * N
    bG = [[] for i in range(N)] # 縮約後のグラフ
    bV = [[] for i in range(N)] # 各ノードに縮約される元頂点
    def dfs(v, prev, edges, conts):
        cur_c = prev_c = 0
        used[v] = True
        conts.append(v)
        for w in G[v]:
            if used[w]:
                if w==prev:
                    # (v, prev)の多重辺対応
                    if prev_c == 1:
                        v_cnts[prev] -= 1
                        cur_c += 1
                    prev_c += 1
                elif not fin[w]:
                    v_cnts[w] -= 1
                    cur_c += 1
            else:
                w_edges = []; w_conts = []
                ret = dfs(w, v, w_edges, w_conts)
                if ret > 0:
                    edges += w_edges
                    conts += w_conts
                else:
                    # (v, w)は橋
                    bG[w] = w_edges
                    for u in w_edges:
                        bG[u].append(w)
                    bV[w] = w_conts
                    edges.append(w)
                cur_c += ret
        fin[v] = True
        cur_c += v_cnts[v]
        return cur_c
    dfs(start, -1, bG[start], bV[start])
    for u in bG[start]:
        bG[u].append(start)
    return bG#, bV

単純にグラフ内で橋となる辺(頂点のペア)を返す実装。

# 単純に橋となる辺を返す
def bridge(G, N):
    result = set()
    label = [None]*N
    gen = 0
    cost = [0]*N
    def dfs(u, p):
        nonlocal gen
        res = 0
        for v in G[u]:
            if v == p:
                continue
            if label[v] is not None:
                if label[v] < label[u]:
                    cost[v] += 1
                    res += 1
            else:
                label[v] = gen; gen += 1
                r = dfs(v, u)
                if r == 0:
                    result.add((u, v) if u < v else (v, u))
                res += r
        res -= cost[u]
        return res
    for v in range(N):
        if not label[v]:
            label[v] = gen; gen += 1
            r = dfs(v, -1)
            assert r == 0, r
    return result