問題
グラフ $(V, E)$ と色集合 $C$ が与えられる。各辺 $e_i$ は色 $c_i$ と重み $w_i > 0$ を持つ。次の条件を両方満たすスパニングフォレストの中で、重み最大のものを求めよ。
- 選んだ辺がスパニングフォレストを形成する(グラフマトロイド)
- 同じ色の辺を 2 本以上選ばない(分割マトロイド)
制約
| パラメータ | 範囲 | 備考 |
|---|---|---|
| $N$ | $2 \le N \le 200$ | 頂点数 |
| $M$ | $1 \le M \le 500$ | 辺数 |
| $c_i$ | $1 \le c_i \le M$ | 色番号 |
| $w_i$ | $1 \le w_i \le 10^4$ | 重み |
入出力例
入力例1
4 5
1 2 1 10
1 3 1 8
2 3 2 6
2 4 3 7
3 4 2 5
出力例1
23
辺 (1,2,色1,w=10)+(2,4,色3,w=7)+(2,3,色2,w=6)=23 が最大。色1・色2・色3 各1本、スパニングフォレスト形成。
概念図: 補助有向グラフ $D_I$
ヒント
ヒント1(方向性)
「スパニングフォレスト ∩ 色上限1」はグラフマトロイドと分割マトロイドの交差問題。重み付きマトロイド交差は補助有向グラフ上の最大重み増加路を繰り返すことで解ける。
ヒント2(アプローチ)
補助有向グラフ $D_I$ を構築し、$S \to T$ への最大重み路 $P$ を Bellman-Ford で求める。$P$ に沿って $I = I \triangle P$ で更新。重みが増加しなくなったら終了。
ヒント3(ほぼ答え)
def add_edge(u, v):
# M1: (I - v + u) がフォレスト?
# M2: (I - v + u) の色制約 OK?
# 補助グラフに辺を追加
...
# Bellman-Ford で最大重み路
d = [-INF] * (M+2)
d[S] = 0
for _ in range(M+2):
for u,v,w in graph_edges:
if d[u] != -INF and d[u]+w > d[v]:
d[v] = d[u]+w; par[v] = u
模範解答
import sys
from collections import defaultdict
def solve():
data = sys.stdin.read().split()
idx = 0
N = int(data[idx]); idx+=1
M = int(data[idx]); idx+=1
edges = []
for _ in range(M):
u=int(data[idx])-1;idx+=1; v=int(data[idx])-1;idx+=1
c=int(data[idx])-1;idx+=1; w=int(data[idx]);idx+=1
edges.append((u,v,c,w))
class DSU:
def __init__(self,n): self.p=list(range(n)); self.r=[0]*n
def find(self,x):
while self.p[x]!=x: self.p[x]=self.p[self.p[x]]; x=self.p[x]
return x
def same(self,x,y): return self.find(x)==self.find(y)
def union(self,x,y):
x,y=self.find(x),self.find(y)
if x==y: return False
if self.r[x]d[v]:
d[v]=d[u]+w; par[v]=u; upd=True
if not upd: break
if d[T]<=0: break
cur=T; path=[]
while cur!=S: path.append(cur); cur=par[cur]
path.reverse()
for node in path:
if node==T: continue
if node in I: I.remove(node); color_cnt[edges[node][2]]-=1
else: I.add(node); color_cnt[edges[node][2]]+=1
print(sum(edges[e][3] for e in I))
solve()
Step-by-Step 解説
Step 1: マトロイド交差とは
2つのマトロイドの共通独立集合を求める問題。多項式時間で解ける(3つ以上はNP困難)。
Step 2: 補助有向グラフ $D_I$
| 辺 | 条件 | 重み |
|---|---|---|
| $S \to x$ | $x \notin I$、$\mathcal{M}_1$ と $\mathcal{M}_2$ どちらも独立 | $+w[x]$ |
| $x \to y$ | $x \notin I, y \in I$、$\mathcal{M}_1$ で $(I-y+x) \in \mathcal{I}_1$ | $w[x]-w[y]$ |
| $y \to x$ | $y \in I, x \notin I$、$\mathcal{M}_2$ で $(I-y+x) \in \mathcal{I}_2$ | $w[x]-w[y]$ |
| $x \to T$ | $x \notin I$、$\mathcal{M}_2$ で $(I+x) \in \mathcal{I}_2$ | $0$ |
Step 3: 最大重み増加路
$S$ から $T$ への最大重み路 $P$ を Bellman-Ford で求め、$I \triangle P$ で更新する。$d[T] \le 0$ なら終了。
Step 4: 計算量
1回の増加路探索は $O(M^2 \cdot (N + M))$(DSU での独立判定を含む)。最大 $M$ 回繰り返すので全体 $O(M^3 (N+M))$。$N, M \le 500$ では十分。
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| マトロイドの方向を混同する | $D_I$ の辺の向きを誤る | $\mathcal{M}_1$ 用と $\mathcal{M}_2$ 用の辺を区別する |
| 増加路の重みが 0 以下でも更新する | 重みが減少する | d[T] > 0 のときのみ更新 |
color_cnt の管理ミス | 分割マトロイドの判定が壊れる | I 更新と同時に color_cnt を維持 |
次のステップ
発展問題: $k$-マトロイド交差(近似アルゴリズム・局所探索法)