-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfeedback_arc_set.py
More file actions
68 lines (51 loc) · 1.64 KB
/
feedback_arc_set.py
File metadata and controls
68 lines (51 loc) · 1.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
r"""Exact dynamic programming for Feedback Arc Set in a directed graph.
Given a directed graph, the \textsc{Feedback Arc Set} problem asks for an
ordering of the vertices minimizing the number of backward edges, that is,
edges $uv$ with $u$ appearing after $v$ in the ordering. Equivalently, one
seeks a minimum set of edges whose removal makes the graph acyclic.
This implementation uses subset DP. Let $\text{dp}(S, v)$ be the minimum
number of backward edges in an ordering of the vertex set $S$ that ends with
$v$. If $v$ is placed last, then every edge from $v$ to a vertex still in $S$
contributes one backward edge, and the remaining vertices are solved
recursively.
Runs in $O(n^2 2^n)$ time.
"""
from functools import cache
Z = frozenset
INF = 10**10
def create_graph(n, edges):
G = [set() for _ in range(n)]
for u, v in edges:
G[u].add(v)
return tuple(map(Z, G))
def backward_edges(G, v, S):
return len(G[v] & S)
def feedback_arc_set(G):
V = Z(range(len(G)))
@cache
def dp(S, v):
if not S:
return 0, None
if v not in S:
return INF, None
if len(S) == 1:
return 0, None
T = S - {v}
best = INF
pred = None
for u in T:
cost, _ = dp(T, u)
if cost < best:
best = cost
pred = u
return best + backward_edges(G, v, T), pred
cost, last = min((dp(V, v)[0], v) for v in V)
order = []
S = V
v = last
while v is not None:
order.append(v)
_, v = dp(S, v)
S = S - {order[-1]}
order.reverse()
return cost, tuple(order)