Skip to content

Commit 0ec4c63

Browse files
authored
Create minimum-operations-to-remove-adjacent-ones-in-matrix.py
1 parent 6459b93 commit 0ec4c63

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Time: O(E * sqrt(V)) = O(m * n * sqrt(m * n))
2+
# Space: O(V) = O(m * n)
3+
4+
from functools import partial
5+
6+
# Time: O(E * sqrt(V))
7+
# Space: O(V)
8+
# Source code from http://code.activestate.com/recipes/123641-hopcroft-karp-bipartite-matching/
9+
# Hopcroft-Karp bipartite max-cardinality matching and max independent set
10+
# David Eppstein, UC Irvine, 27 Apr 2002
11+
def bipartiteMatch(graph):
12+
'''Find maximum cardinality matching of a bipartite graph (U,V,E).
13+
The input format is a dictionary mapping members of U to a list
14+
of their neighbors in V. The output is a triple (M,A,B) where M is a
15+
dictionary mapping members of V to their matches in U, A is the part
16+
of the maximum independent set in U, and B is the part of the MIS in V.
17+
The same object may occur in both U and V, and is treated as two
18+
distinct vertices if this happens.'''
19+
20+
# initialize greedy matching (redundant, but faster than full search)
21+
matching = {}
22+
for u in graph:
23+
for v in graph[u]:
24+
if v not in matching:
25+
matching[v] = u
26+
break
27+
28+
while 1:
29+
# structure residual graph into layers
30+
# pred[u] gives the neighbor in the previous layer for u in U
31+
# preds[v] gives a list of neighbors in the previous layer for v in V
32+
# unmatched gives a list of unmatched vertices in final layer of V,
33+
# and is also used as a flag value for pred[u] when u is in the first layer
34+
preds = {}
35+
unmatched = []
36+
pred = dict([(u,unmatched) for u in graph])
37+
for v in matching:
38+
del pred[matching[v]]
39+
layer = list(pred)
40+
41+
# repeatedly extend layering structure by another pair of layers
42+
while layer and not unmatched:
43+
newLayer = {}
44+
for u in layer:
45+
for v in graph[u]:
46+
if v not in preds:
47+
newLayer.setdefault(v,[]).append(u)
48+
layer = []
49+
for v in newLayer:
50+
preds[v] = newLayer[v]
51+
if v in matching:
52+
layer.append(matching[v])
53+
pred[matching[v]] = v
54+
else:
55+
unmatched.append(v)
56+
57+
# did we finish layering without finding any alternating paths?
58+
if not unmatched:
59+
unlayered = {}
60+
for u in graph:
61+
for v in graph[u]:
62+
if v not in preds:
63+
unlayered[v] = None
64+
return (matching,list(pred),list(unlayered))
65+
66+
# recursively search backward through layers to find alternating paths
67+
# recursion returns true if found path, false otherwise
68+
def recurse(v):
69+
if v in preds:
70+
L = preds[v]
71+
del preds[v]
72+
for u in L:
73+
if u in pred:
74+
pu = pred[u]
75+
del pred[u]
76+
if pu is unmatched or recurse(pu):
77+
matching[v] = u
78+
return 1
79+
return 0
80+
81+
def recurse_iter(v):
82+
def divide(v):
83+
if v not in preds:
84+
return
85+
L = preds[v]
86+
del preds[v]
87+
for u in L :
88+
if u in pred and pred[u] is unmatched: # early return
89+
del pred[u]
90+
matching[v] = u
91+
ret[0] = True
92+
return
93+
stk.append(partial(conquer, v, iter(L)))
94+
95+
def conquer(v, it):
96+
for u in it:
97+
if u not in pred:
98+
continue
99+
pu = pred[u]
100+
del pred[u]
101+
stk.append(partial(postprocess, v, u, it))
102+
stk.append(partial(divide, pu))
103+
return
104+
105+
def postprocess(v, u, it):
106+
if not ret[0]:
107+
stk.append(partial(conquer, v, it))
108+
return
109+
matching[v] = u
110+
111+
ret, stk = [False], []
112+
stk.append(partial(divide, v))
113+
while stk:
114+
stk.pop()()
115+
return ret[0]
116+
117+
for v in unmatched: recurse_iter(v)
118+
119+
120+
class Solution(object):
121+
def minimumOperations(self, grid):
122+
"""
123+
:type grid: List[List[int]]
124+
:rtype: int
125+
"""
126+
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
127+
def iter_dfs(grid, i, j, lookup, adj):
128+
if lookup[i][j]:
129+
return
130+
lookup[i][j] = True
131+
stk = [(i, j, (i+j)%2)]
132+
while stk:
133+
i, j, color = stk.pop()
134+
for di, dj in directions:
135+
ni, nj = i+di, j+dj
136+
if not (0 <= ni < len(grid) and 0 <= nj < len(grid[0]) and grid[ni][nj]):
137+
continue
138+
if not color:
139+
adj[len(grid[0])*ni+nj].append(len(grid[0])*i+j)
140+
if lookup[ni][nj]:
141+
continue
142+
lookup[ni][nj] = True
143+
stk.append((ni, nj, color^1))
144+
145+
adj = collections.defaultdict(list)
146+
lookup = [[False]*len(grid[0]) for _ in xrange(len(grid))]
147+
for i in xrange(len(grid)):
148+
for j in xrange(len(grid[0])):
149+
if not grid[i][j]:
150+
continue
151+
iter_dfs(grid, i, j, lookup, adj)
152+
return len(bipartiteMatch(adj)[0])

0 commit comments

Comments
 (0)