```python= import sys class Matrix(): """ A square matrix with n by n elements """ def __init__(self, n, A=None): if A is None: A = [n * [0] for _ in range(n)] self.A = A self.n = n def set(self, i, j, val): """ sets A_ij to val """ self.A[i][j] = val def get(self, i, j): """ returns the value of the A_ij """ return self.A[i][j] def copy(self): """ returns a deep copy of the matrix """ A = [l.copy() for l in self.A] return Matrix(self.n, A=A) def __repr__(self): return f'<Matrix n:{self.n}>' def __str__(self): s = ' ' + ' '.join([str(i) for i in range(self.n)]) + ' \n' s += ' --' + (2 * self.n) * '-' + '\n' for i, row in enumerate(self.A): s += f'{i} |' for col in row: s += str(col) + ' ' s += '|\n' s += ' --' + (2 * self.n) * '-' + '\n' return s class BipartiteGraph(): """ A bipartite graph with as many edges on each side. The source has index 0, the sink has index (n + 1). The vertices in U have index 1 to n, and in V have index n + 1 to 2n. """ def __init__(self, n, adj=None, weights=None): """ Initializes a bipartite graph with n vertices on each side and no edges """ if adj is None: adj = Matrix(2*n + 2) if weights is None: weights = Matrix(2*n + 2) self.n = n self.adj_matrix = adj self.weight_matrix = weights source = 0 # Connect Source with weight 2 for ui in range(1, self.n+1): self.adj_matrix.set(source, ui, 1) self.weight_matrix.set(source, ui, 2) # Connect Sink with weight 2 sink = (2 * self.n) + 1 for i in range(self.n+1, 2*self.n + 1): self.adj_matrix.set(i, sink, 1) self.weight_matrix.set(i, sink, 2) def add_edge(self, u, v, w=1): """ Add an edge from u to v with weight w. """ self.adj_matrix.set(u, v, 1) self.weight_matrix.set(u, v, w) def get_weight(self, u, v): """ return: int. the weight of the edge (u, v) """ if self.adj_matrix.get(u, v) == 1: return self.weight_matrix.get(u, v) else: return None def get_adjacency_list(self, u): """ return: list(int). the list of vertices that are adjacent to u """ return [i for i, val in enumerate(self.adj_matrix.A[u]) if val == 1] def get_adjacency_matrix(self): """ return: Matrix(n). The adjacency matrix for this graph """ return self.adj_matrix def set_weight(self, u, v, w): """ sets the weight of edge (u, v) to w. """ if self.adj_matrix.get(u, v) == 1: self.weight_matrix.set(u, v, w) def residual_network(self, flow): """ Computes the residual network for the graph. Does not copy the adj matrix (for performance reasons) so don't modify its values in the residual network if you need to use it again. param flow: Matrix(2n + 2) return: BipartiteGraph. The residual network """ residual_weights = Matrix(2 * self.n + 2) # Compute residual network from s <-> ui source = 0 for i in range(self.n + 1): f = flow.get(source, i) c = self.weight_matrix.get(source, i) res_flow = c - f if res_flow > 0: residual_weights.set(source, i, res_flow) if f > 0: residual_weights.set(i, source, f) # Compute residual network from ui <-> vj source = 0 for ui in range(1, self.n + 1): for vj in range(self.n + 1, 2*self.n + 1): f = flow.get(ui, vj) c = self.weight_matrix.get(ui, vj) res_flow = c - f if res_flow > 0: residual_weights.set(ui, vj, res_flow) if f > 0: residual_weights.set(vj, ui, f) # Compute residual network from vj <-> t sink = 2 * self.n + 1 for i in range(self.n + 1, 2*self.n + 1): f = flow.get(i, sink) c = self.weight_matrix.get(i, sink) res_flow = c - f if res_flow > 0: residual_weights.set(i, sink, res_flow) if f > 0: residual_weights.set(sink, i, f) return BipartiteGraph(self.n, adj=self.adj_matrix, weights=residual_weights) def copy(self, zero_weights=True): """ If zero_weights, all weights are set to 0 (including the ones connecting the source and sink). return: BipartiteGraph. A copy of this graph. """ adj_mat = self.adj_matrix.copy() if zero_weights: weight_mat = Matrix(self.n + 2) else: weight_mat = self.weight_matrix.copy() return BipartiteGraph(self.n, adj=adj_mat, weights=weight_mat) def __repr__(self): return f'<BipartiteGraph n:{self.n}>' def read_inputs(): """ Reads the input to the program from stdin. Computes the adjacency matrix A for the graph returns: n, m, A """ n, m = [int(v) for v in sys.stdin.readline().split(' ')] A = Matrix(n) for i in range(m): ui, vi = [int(v) for v in sys.stdin.readline().split(' ')] A.set(ui, vi, 1) return n, m, A """ BFS takes a BipartiteGraph, a starting node and returns a path. Path is empty if there is no connection between source and destination vertices """ def bfs2(residual_graph): source = 0 sink = 2 * residual_graph.n + 1 path = {} # for each node, path[node] = parent queue = [source] visited = set([source]) while len(queue) > 0: # pop first node from the queue curr_node = queue.pop(0) for neighbour in residual_graph.get_adjacency_list(curr_node): if neighbour not in visited and residual_graph.get_weight(curr_node, neighbour) > 0: queue.append(neighbour) path[neighbour] = curr_node visited.add(neighbour) # compute shortest path to sink, if one was found path_found = None if (sink in visited): p = sink path_found = [sink] while path.get(p) is not None and path.get(p) != source: p = path.get(p) path_found = [p] + path_found path_found = [source] + path_found # return true if we reached sink from source else false # and the list of nodes to follow from source to sink return (sink in visited), path_found def example_bfs(): n = 3 g = BipartiteGraph(n=n) g.add_edge(1, n + 1) g.add_edge(2, n + 2) g.add_edge(2, n + 3) g.add_edge(3, n + 3) print(f'Graph: {g}') print('Adjacency Matrix:') print(g.adj_matrix) print('Weight Matrix:') print(g.weight_matrix) flow = Matrix(2 * n + 2) residual = g.residual_network(flow) print('\n\n\n') print('Residual Graph:') print(residual.adj_matrix) print(residual.weight_matrix) print('\n\n\n') print('BFS:') found, path = bfs(residual) print(' -> '.join([str(v) for v in path])) def read_inputs(): """ Reads the input to the program from stdin. Computes the adjacency matrix A for the graph returns: n, m, A """ n, m = [int(v) for v in sys.stdin.readline().split(' ')] A = Matrix(n) for i in range(m): ui, vi = [int(v) for v in sys.stdin.readline().split(' ')] A.set(ui, vi, 1) return n, m, A def ford_fulkerson(g): flow = Matrix(2 * n + 2) residual = g.residual_network(flow) pathExists, path = bfs2(residual) source = 0 max_flow=0 while(pathExists){ curr_node = source #=0 path_capacity = sys.maxsize for neighbour in path: nc = residual_graph.get_weight(curr_node, neighbour) path_capacity = min(path_capacity,nc) curr_node = neighbour max_flow +=path_capacity curr_node = source #=0 for neighbour in path: f_uv = flow.get(curr_node, neighbour) f_vu = flow.get(neighbour, curr_node) flow.set(curr_node, neighbour,f_uv + path_capacity) flow.set(neighbour, curr_node,f_vu + path_capacity) pathExists, path = bfs2(residual) residual = g.residual_network(flow) } return max_flow """ BFS takes a BipartiteGraph, a starting node and returns a path. Path is empty if there is no connection between source and destination vertices"""" def bfs(graph, residual_graph, path, source, dest): # for each node, path[node] = parent queue = [source] visited = set(source) while len(queue) > 0: #pop first node from the queue curr_node = queue.pop(0) for neighbour in graph.adjacencyList(curr_node): if neighbour not in explored and residual_graph[curr_node][neighbour] > 0: queue.append(neigbour) path[neighbour] = current_node visited.add(neighbour) #return true if we reached sink from source else false return visited.get(dest,False) ```