def find_matching(self): u"""Finds a matching in the bipartite graph. This is done using the Hopcroft-Karp algorithm with an implementation from the `hopcroftkarp` package. Returns: A dictionary where each edge of the matching is represented by a key-value pair with the key being from the left part of the graph and the value from te right part. """ # The directed graph is represented as a dictionary of edges # The key is the tail of all edges which are represented by the value # The value is a set of heads for the all edges originating from the tail (key) # In addition, the graph stores which part of the bipartite graph a node originated from # to avoid problems when a value exists in both halfs. # Only one direction of the undirected edge is needed for the HopcroftKarp class directed_graph = {} # type: Dict[Tuple[int, TLeft], Set[Tuple[int, TRight]]] for (left, right) in self._edges: tail = (LEFT, left) head = (RIGHT, right) if tail not in directed_graph: directed_graph[tail] = set([head]) else: directed_graph[tail].add(head) matching = HopcroftKarp(directed_graph).maximum_matching() # Filter out the partitions (LEFT and RIGHT) and only return the matching edges # that go from LEFT to RIGHT return dict((tail[1], head[1]) for tail, head in matching.items() if tail[0] == LEFT)
def get_departure_keys_product(rules=RULES, tickets=TICKETS, my_ticket=MY_TICKET): tickets_to_remove = set(i for i, ticket in enumerate(tickets) if invalid(ticket, rules)) n = len(tickets[0]) rule_columns = {r: set(range(n)) for r in rules} for i, ticket in enumerate(tickets): if i in set(tickets_to_remove): continue for rule in rules: for j, v in enumerate(ticket): if not is_valid(v, rules[rule]): rule_columns[rule].discard(j) matching = HopcroftKarp(rule_columns).maximum_matching(keys_only=True) departure_keys = [i for k, i in matching.items() if "departure" in k] return product([my_ticket[i] for i in departure_keys])