問題
N 頂点 M 辺の有向グラフ。各辺 i は容量 $c_i$、下限流量 $l_i$、コスト $w_i$。各辺に流量 $f_i$($l_i \le f_i \le c_i$)を割り当て、全頂点でフロー保存則を満たし、コスト $\sum w_i f_i$ を最小化(ソース・シンクなし、循環流)。
制約
$2 \le N \le 200$
$1 \le M \le 2000$
$0 \le l_i \le c_i \le 10^4$
$-10^4 \le w_i \le 10^4$
実行可能解が存在
入出力例
入力例 1
4 5
1 2 1 3 2
2 3 0 2 -1
3 4 1 4 3
4 1 0 3 1
1 3 0 2 5
出力例 1
5
ヒント (段階的開示)
ヒント1: 方向性
下限付き循環最小費用流問題。下限を満たす初期解を構築し、最小費用流で最適化。
ヒント2: アプローチ
$x_i = f_i - l_i$ に変換。超源泉 S、超シンク T を導入し、各頂点の供給/需要を計算してフロー問題に帰着。
ヒント3: 誘導
for u, v, l, c, w in edges:
demand[v] += l
demand[u] -= l
# demand > 0 の頂点へ S から、 < 0 から T へ辺追加
模範解答 (Python)
import sys
from collections import defaultdict, deque
input = sys.stdin.readline
class MCMF:
def __init__(self, n):
self.n = n
self.graph = [[] for _ in range(n)]
def add_edge(self, u, v, cap, cost):
self.graph[u].append([v, cap, cost, len(self.graph[v])])
self.graph[v].append([u, 0, -cost, len(self.graph[u]) - 1])
def min_cost_flow(self, s, t, max_flow):
INF = float('inf')
flow = cost = 0
while flow < max_flow:
dist = [INF] * self.n
dist[s] = 0
in_queue = [False] * self.n
prev_v = [-1] * self.n
prev_e = [-1] * self.n
q = deque([s]); in_queue[s] = True
while q:
v = q.popleft(); in_queue[v] = False
for i, (nv, cap, c, _) in enumerate(self.graph[v]):
if cap > 0 and dist[v] + c < dist[nv]:
dist[nv] = dist[v] + c
prev_v[nv] = v; prev_e[nv] = i
if not in_queue[nv]:
q.append(nv); in_queue[nv] = True
if dist[t] == INF:
break
d = max_flow - flow
v = t
while v != s:
d = min(d, self.graph[prev_v[v]][prev_e[v]][1])
v = prev_v[v]
v = t
while v != s:
self.graph[prev_v[v]][prev_e[v]][1] -= d
self.graph[v][self.graph[prev_v[v]][prev_e[v]][3]][1] += d
v = prev_v[v]
flow += d
cost += d * dist[t]
return flow, cost
def solve():
N, M = map(int, input().split())
edges = []
for _ in range(M):
u, v, l, c, w = map(int, input().split())
edges.append((u-1, v-1, l, c, w))
S = N; T = N + 1
mcmf = MCMF(N + 2)
demand = [0] * N
base_cost = 0
for u, v, l, c, w in edges:
mcmf.add_edge(u, v, c - l, w)
demand[v] += l
demand[u] -= l
base_cost += w * l
total_supply = 0
for i in range(N):
if demand[i] > 0:
mcmf.add_edge(S, i, demand[i], 0)
total_supply += demand[i]
elif demand[i] < 0:
mcmf.add_edge(i, T, -demand[i], 0)
flow, cost = mcmf.min_cost_flow(S, T, total_supply)
print(base_cost + cost)
solve()
Step-by-Step 解説
1下限付きフロー変換
$x_i = f_i - l_i$ で容量を $[0, c_i - l_i]$ に。
$x_i = f_i - l_i$ で容量を $[0, c_i - l_i]$ に。
2需要計算
下限分を強制的に流したとみなして各頂点のバランスを記録。
下限分を強制的に流したとみなして各頂点のバランスを記録。
3S/T 接続
需要 > 0 の頂点に S から、< 0 から T へ辺追加。最大流 = total_supply で実行可能。
需要 > 0 の頂点に S から、< 0 から T へ辺追加。最大流 = total_supply で実行可能。
4MCMF で最適化
負辺コストがあるため SPFA を使用。
負辺コストがあるため SPFA を使用。
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| 負コストに Dijkstra | 正しく動かない | SPFA か Johnson 変換 |
| base_cost 計算忘れ | 下限分を加算し忘れ | base_cost += w * l |
| 需要の符号ミス | v への流入 +l, u から -l | demand[v] += l; demand[u] -= l |
次のステップ
- ソース・シンクがある場合の下限付き最小費用流(S→T 辺補完)