Question 1: def MinNumAirports(G=(A,B,E)): construct G' from bipartite graph G -> O(n+m) # run a modified Ford-Fulkerson algorithm, in which it returns the max flow graph, as well as its corresponding residual graph F, R_f = FordFulkerson(G') -> O(km) # we use the R_f to find set S that has maximum capacity, do so with with BFS from source node s, add all reachable nodes from S S = BFS(R_f, s) -> O(n+m) minAirportsMachines = set() # can apply kΓΆnig's theorem to find min vertex cover for v in A U B: -> O(n) iterations if v in A and v not in S: -> O(1) minAirporsMachines.add(v) -> O(1) elif v in B and v in S: -> O(1) minAirporsMachines.add(v) -> O(1) return minAirportsMachines # we assume we are working with sets implemented via hash tables -> Total Run Time: O(n+m) + O(km) + O(n+m) + O(n) β O(n+m) + O(nm) + O(n+m) + O(n) β O(nm) Question 2: def FindOptimalPath(A): # we construct a graph G by iterating through every 2 vertices V = set() E = set() for i = 1 ... n: -> O(n) iterations for j = 1 ... n: -> O(n) iterations s1 = str(A[i]) -> O(k) s2 = str(A[j]) -> O(k) if s1 and s2 differ by exactly one digit: -> O(k) with below being O(1) add A[i] to V add A[j] to V add (s1,s2) to E with edge weight 2A[j] - A[i] add (s2,s1) to E with edge weight 2A[i] - A[j] # now, we have constructed the graph G = (V,E). Now, we run Bellman-Ford algorithm, with n = |V| and starting node as A[1] dist, pred = BellmanFord(len(V), E, A[1]) -> O(n(n^2 + kn)) (explained below) # with the pred array -> traverse backwards to obtain the path st = stack() sol = [] curr = n # will break out when we reach node with no pred (cur == NULL) while curr: -> O(n) if dist[curr] is infinity: return IMPOSSIBLE push A[curr] onto the st stack curr = pred[curr] while st not empty: -> O(n) sol.append(st.pop()) return sol -> Total Run Time: O(kn^2) + O(n(n^2 + kn)) + O(n) + O(n) β O(n^3 + kn^2) Question 3: def MinTrainPath(w): D0 = [1...n][1...n][2] # instead of simply copying weight matrix for base case -> need to iterate through for i = 1 ... n: -> O(n) iterations for j = 1 ... n: -> O(n) iterations # these are the 2 sub cases seen in the base case D0[i][j][0] = w[i,j] -> below in this loop is constant work if w[i,j] is infinity D0[i][j][0] = infinity else: D0[i][j][1] = 0 D1 = [1...n][1...n][2] Dlast = pointer to D0 DCurr = pointer to D1 # for all interior nodes for k = 1 ... n: -> O(n) iterations for i = 1 ... n: -> O(n) iterations for j = 1 ... n: -> O(n) iterations Dcurr[i,j,0] = min(Dlast[i,j,0], Dlast[i,k,0] + Dlast[k,j,0]) -> constant work Dcurr[i,j,1] = min(Dlast[i,j,1], Dlast[i,k,0] + Dlast[k,j,1], Dlast[i,k,1] + Dlast[k,j,0]) -> constant work temp = DLast Dlast = Dcurr Dcurr = temp # construct solution array Dn = [1...n][1...n] for i = 1 ... n: -> O(n) iterations for j = 1 ... n: -> O(n) iterations # we look at Dlast, as it swapped over at the very end Dn[i][j] = Dlast[i][j][1] -> constant work return Dn -> Total Run Time: O(n^2) + O(n^3) + O(n^2) β O(n^3) Question 4: def MonotonicBelmanFord(G=(V,E;w),n,m,s): pred[1...n] = [null, null, ..., null] D[1...n] = [infty, infty, ..., infty] prevEdgeWeight[1...n] = [null, null, .., null] D[s] = 0 for i = 1 ... n: -> O(n) iterations for (u,v,w) in E: -> O(m) iterations if D[u] + w < D[v]: if w > prevEdgeWeight[u] or prevEdgeWeight[u] is null: D[v] = D[u] + w pred[v] = u prevEdgeWeight[v] = w return (D, pred) -> Total Run Time: O(nm)