-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstoer-wagner.py
More file actions
146 lines (105 loc) · 3.69 KB
/
stoer-wagner.py
File metadata and controls
146 lines (105 loc) · 3.69 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# Tworzy podzbiory S, T (wierzchołków), których waga minimalnego cięcia jest minimalna
# (czyli suma wag krawędzi, których usunięcie je rozspójni jest najmniejsza)
# na zdjęciu przykład jak to działa
from heapq import heappop, heappush
from checker import check
class heap_tmp:
def __init__(self, w, v):
self.w = w
self.v = v
def __lt__(self, other):
return self.w > other.w # i teraz nie trzeba zmieniać wartości na -
class hashed_maxheap:
def __init__(self):
self.PQ = []
self.added_weights = {}
def push(self, w, v):
if v in self.added_weights:
w += self.added_weights[v]
self.added_weights[v] = w
heappush(self.PQ, heap_tmp(w, v))
def pop(self):
while len(self.PQ) > 0:
res = heappop(self.PQ)
w, v = res.w, res.v
if self.added_weights[v] == w:
break # rozwiązanie problemu duplikatów w kolejce, lazy deletion
return w, v
def is_empty(self):
return len(self.PQ) == 0
class Node:
def __init__(self):
self.edges = {} # słownik mapujący wierzchołki do których są krawędzie na ich wagi
def addEdge( self, to, weight):
self.edges[to] = self.edges.get(to,0) + weight # dodaj krawędź do zadanego wierzchołka
# o zadanej wadze; a jeśli taka krawędź
# istnieje, to dodaj do niej wagę
def delEdge( self, to ):
del self.edges[to] # usuń krawędź do zadanego wierzchołka
def gen_adj_dict(L):
n = 0
for x, y, _ in L:
x -= 1
y -= 1
if x > n: n = x
if y > n: n = y
n += 1
G = {Node(): i for i in range(n)}
nodes = list(G.keys())
for x, y, c in L:
x -= 1
y -= 1
nodes[x].addEdge(nodes[y], c)
nodes[y].addEdge(nodes[x], c)
return G
# etap 1
# funkcja, która zwraca nam 2 wierzchołki do scalenia, o danej wadze cięcia je rozspójniającego
# realizacja z kopcem, hashmapą (sprawdzamy jej długość i jak == 1 to zwracamy parę (wierzchołek nie w kopcu, pierwszy wierzchołek z kopca))
# z wagą równą sumie wag krawędzi wychodzących z wierzchołka, który nie jest w kopcu (czyli tego co zostanie)
def min_cut_phase(G : dict):
H = hashed_maxheap()
nodes = list(G.keys())
H.push(0, nodes[0])
# G to lista node'ów
processed = set()
last = None
prev = None
while not H.is_empty():
u: Node
_, u = H.pop()
if u in processed: continue
processed.add(u)
prev = last
last = u
for v in u.edges.keys():
if v not in processed:
H.push(u.edges[v], v)
# prev to wierzchołek co był ostatni zdjęty ze stosu
# last to ten, który pozostał na sam koniec (singleton)
last : Node
# od pythona 3.7 kolejność w kluczy w słowniku jest zgodna z ich dodawaniem
cut_weight = 0
for w in last.edges.values():
cut_weight += w
return prev, last, cut_weight
# etap 2
# łączenie par wierzchołków zwracanych przez etap 1
def merge_vertices(G : dict, x : Node, y : Node):
for u in y.edges.keys():
u : Node
if u != x:
x.addEdge(u, y.edges[u])
u.delEdge(y)
u.addEdge(x, y.edges[u])
if y in x.edges:
x.delEdge(y)
del G[y]
def stoer_wagner(L):
G = gen_adj_dict(L)
min_cut = float('inf')
while len(G) > 1:
v, u, w = min_cut_phase(G)
if w < min_cut: min_cut = w
merge_vertices(G, v, u)
return min_cut
check(stoer_wagner)