Minimum Steiner Tree in Graphs
概要
重み付き無向グラフ \(G = (V, E)\) を考える。
ある \(S \subseteq V\) に対するシュタイナー木は、頂点集合 \(S\) を含む木である。
このとき、 \(S\) に含まれる頂点は terminal と呼ぶ。
最小シュタイナー木問題では、頂点集合 \(S\) に対するシュタイナー木のうち、辺の重みの総和が最小となる木を求める。
計算量
\(O(|V|^3 + |V| 3^{|S|} + |V|^2 2^{|S|})\)
実装
INF = 10**9
def minimum_steiner_tree(N, G, L, V):
E = [[INF]*N for i in range(N)]
for v in range(N):
for w, d in G[v]:
E[v][w] = d
E[v][v] = 0
for k in range(N):
Ek = E[k]
for Ei in E:
v = Ei[k]
Ei[:] = (min(a, v + b) for a, b in zip(Ei, Ek))
S = [[INF]*(1 << L) for i in range(N)]
for i in range(L):
state = (1 << i)
for v in range(N):
S[v][state] = E[v][V[i]]
S[V[i]][state] = 0
for i in range(N):
S[i][0] = 0
def gen(state):
s0 = (state - 1) & state
while s0 != state:
if s0 < state:
yield s0
s0 = (s0 - 1) & state
us = [INF]*N
infs = [INF]*N
for state in range(1, 1 << L):
ss = list(gen(state))
us[:] = infs
for v in range(N):
Sv = S[v]
u = min(Sv[s0] + Sv[s0 ^ state] for s0 in ss)
us[:] = (min(a, b + u) for a, b in zip(us, E[v]))
for w in range(N):
S[w][state] = us[w]
ALL = (1 << L) - 1
return min(S[v][ALL] for v in range(N))
N = 5
G = [
[(1, 1), (2, 2), (3, 7)],
[(0, 1), (3, 4)],
[(0, 2), (3, 2)],
[(0, 7), (1, 4), (2, 2), (4, 1)],
[(3, 1)],
]
L = 2
V = [0, 3]
print(minimum_steiner_tree(N, G, L, V))
# => "4"
Verified
-
AOJ: "1040: Chocolate with Heart Marks": source (Python3, 12.76sec)
参考
-
Dreyfus, Stuart E., and Robert A. Wagner. "The Steiner problem in graphs." Networks 1.3 (1971): 195-207.