問題
$N$ 個の要素からなる集合 $U = \{0, 1, \ldots, N-1\}$ がある。2つのマトロイド $M_1, M_2$ が与えられ、それぞれ独立集合族を持つ。
$M_1$: グラフ的マトロイド(Graphic Matroid)。$N$ 要素は辺に対応し、グラフ $G$ の辺集合 $E$ から森(閉路を含まない部分グラフ)を選ぶ。
$M_2$: 分割マトロイド(Partition Matroid)。$N$ 要素は $K$ グループ $G_1, G_2, \ldots, G_K$ に分割されており、各グループ $G_i$ から高々 $k_i$ 個選べる。
両マトロイドの独立集合の共通集合(Matroid Intersection)の最大サイズを求めよ。
入力形式
V E
u_1 v_1
u_2 v_2
...
u_E v_E
K
g_1 k_1
g_2 k_2
...
g_E k_E
制約
$2 \leq V \leq 100$
$1 \leq E \leq 500$
$1 \leq K \leq E$
$1 \leq k_i \leq E$
入出力例
入力例 1
4 5
0 1
0 2
1 2
1 3
2 3
3
0 2
0 2
1 1
2 1
3 1
出力例 1
3
ヒント (段階的開示)
ヒント1: 方向性
マトロイド交差アルゴリズムは「交換グラフ上の最短路」を繰り返し見つけることで最大独立集合を拡大する。
ヒント2: アプローチ
現在の共通独立集合 $I$ から: (1) $M_1$ で $I$ に追加できる要素 $X_1 = \{e \notin I : I + e \in \mathcal{I}_1\}$、(2) $M_2$ で追加できる要素 $X_2 = \{e \notin I : I + e \in \mathcal{I}_2\}$、(3) 交換グラフ $D_{M_1,M_2}(I)$ を構築して $X_1 \to X_2$ の最短路を探す。
ヒント3: 誘導
# 交換グラフの辺:
# e ∈ I → f ∉ I: I - e + f ∈ M1 のとき e→f
# f ∉ I → e ∈ I: I - e + f ∈ M2 のとき f→e
# BFS で X1 の要素から X2 の要素への最短路を見つけ、
# その路上で I の要素とそれ以外を交互にswapする
模範解答 (Python)
import sys
from collections import deque
def solve():
data = sys.stdin.read().split()
pos = 0
V = int(data[pos]); pos += 1
E = int(data[pos]); pos += 1
edges = []
for _ in range(E):
u = int(data[pos]); pos += 1
v = int(data[pos]); pos += 1
edges.append((u, v))
K = int(data[pos]); pos += 1
group = [0] * E
k_limit = {}
for i in range(E):
g = int(data[pos]); pos += 1
ki = int(data[pos]); pos += 1
group[i] = g
k_limit[g] = ki
def make_uf(n):
return list(range(n))
def find(uf, x):
while uf[x] != x:
uf[x] = uf[uf[x]]
x = uf[x]
return x
def union(uf, x, y):
x, y = find(uf, x), find(uf, y)
if x == y:
return False
uf[x] = y
return True
def is_independent_M1(S):
uf = make_uf(V)
for e in S:
u, v = edges[e]
if not union(uf, u, v):
return False
return True
def can_add_M1(S_set, e):
uf = make_uf(V)
for f in S_set:
union(uf, edges[f][0], edges[f][1])
u, v = edges[e]
return find(uf, u) != find(uf, v)
def can_exchange_M1(S_set, e, f):
new_S = (S_set - {e}) | {f}
return is_independent_M1(new_S)
def can_add_M2(S_set, e):
g = group[e]
cnt = sum(1 for f in S_set if group[f] == g)
return cnt < k_limit[g]
def can_exchange_M2(S_set, e, f):
new_S = (S_set - {e}) | {f}
for g, lim in k_limit.items():
if sum(1 for x in new_S if group[x] == g) > lim:
return False
return True
I = set()
while True:
X1 = set(e for e in range(E) if e not in I and can_add_M1(I, e))
X2 = set(e for e in range(E) if e not in I and can_add_M2(I, e))
common = X1 & X2
if common:
I.add(next(iter(common)))
continue
dist = {e: -1 for e in range(E)}
prev = {}
queue = deque()
for s in X1:
dist[s] = 0
queue.append(s)
found = None
while queue and found is None:
cur = queue.popleft()
if cur not in I:
for e in list(I):
if can_exchange_M2(I, e, cur) and dist[e] == -1:
dist[e] = dist[cur] + 1
prev[e] = cur
queue.append(e)
else:
for f in range(E):
if f not in I and dist[f] == -1 and can_exchange_M1(I, cur, f):
dist[f] = dist[cur] + 1
prev[f] = cur
if f in X2:
found = f
break
queue.append(f)
if found is None:
break
path = []
node = found
while node in prev:
path.append(node)
node = prev[node]
path.append(node)
for node in path:
if node in I:
I.remove(node)
else:
I.add(node)
print(len(I))
solve()
Step-by-Step 解説
1マトロイドとは
独立集合族 $\mathcal{I}$ が以下を満たす組み合わせ構造: (1) $\emptyset \in \mathcal{I}$、(2) $A \in \mathcal{I}, B \subseteq A \Rightarrow B \in \mathcal{I}$(遺伝性)、(3) $A, B \in \mathcal{I}, |A| < |B| \Rightarrow \exists e \in B \setminus A: A+e \in \mathcal{I}$(拡張性)。
独立集合族 $\mathcal{I}$ が以下を満たす組み合わせ構造: (1) $\emptyset \in \mathcal{I}$、(2) $A \in \mathcal{I}, B \subseteq A \Rightarrow B \in \mathcal{I}$(遺伝性)、(3) $A, B \in \mathcal{I}, |A| < |B| \Rightarrow \exists e \in B \setminus A: A+e \in \mathcal{I}$(拡張性)。
2交換グラフの構築
現在の共通独立集合 $I$ に対し、有向グラフ $D$ を構築: $e \in I, f \notin I$ で $I - e + f \in \mathcal{I}_1$ なら弧 $e \to f$。$I - e + f \in \mathcal{I}_2$ なら弧 $f \to e$。
現在の共通独立集合 $I$ に対し、有向グラフ $D$ を構築: $e \in I, f \notin I$ で $I - e + f \in \mathcal{I}_1$ なら弧 $e \to f$。$I - e + f \in \mathcal{I}_2$ なら弧 $f \to e$。
3BFS で拡大路を探す
「$M_1$ で追加可能な要素 $X_1$」から「$M_2$ で追加可能な要素 $X_2$」への最短路を BFS で探す。路が存在すれば $I$ を拡大できる。
「$M_1$ で追加可能な要素 $X_1$」から「$M_2$ で追加可能な要素 $X_2$」への最短路を BFS で探す。路が存在すれば $I$ を拡大できる。
4計算量
各反復で $|I|$ が 1 増える。最大 $r = \min(r_1, r_2)$ 回の反復。各反復で BFS: $O(E^2 \cdot \text{oracle})$ → 全体 $O(r \cdot E^2 \cdot \text{oracle})$。
各反復で $|I|$ が 1 増える。最大 $r = \min(r_1, r_2)$ 回の反復。各反復で BFS: $O(E^2 \cdot \text{oracle})$ → 全体 $O(r \cdot E^2 \cdot \text{oracle})$。
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| X1∩X2 のチェックを省く | 直接追加できる場合を見逃す | BFS前にcommonをチェック |
| 交換グラフの弧方向を逆にする | M1とM2の役割を混同 | e→f (M1), f→e (M2) |
| 路の更新でI⊕pathを間違える | 対称差を取り忘れ | 路上のノードをXOR的に切り替え |
次のステップ
- 発展問題: 重み付きマトロイド交差(最大重みの共通独立集合)
- 応用: 有向全域木(Directed Spanning Tree / Matroid Union)