You can create this residual graph with two augmenting paths: S -> B -> E -> T (2 units of flow), and S -> A -> C -> D -> E -> T (60 units of flow).
Part B: 62.
Part C: The edges D -> C and B -> E compose the minimum cut.
Part D: This is the minimum hop path, which is S -> B -> E -> T. The resulting residual graph is graph A.
Part E: This picks the path by performing a depth-first search, traversing each node's edge list in descending order of edge capacity. So, S -> A, then A -> C, then C -> D, then D -> B, then B -> E and E -> T. Just two units of flow. Note, if we hadn't chosen that D -> B edge, we would have done better. Regardless, the residual is C.
Part F: Here, we choose the maximum flow path: S -> A -> C -> D -> E -> T: 60 units of flow. The residual is graph H.
Part G: We start with the final residual flow graph (the answer to A), and we partition the graph into two sets: nodes reachable from S and nodes not reachable from S. The first set is { S, A, B, C } and the second is { D, E, T }. The minimum cut is all edges in the original graph from one set to the other -- those are edges C -> D and B -> E. of flow.
Part B: Here's the rendering of the algorithm at each step. The table states the minimum known distance to each node at each step. We underline the distance at the point when the node enters the minimum spanning tree:
Step | S | Z | Y | X | W | V | T | Edge Added |
Start | 0 | - | - | - | - | - | - | - |
1 | 0 | 12 | 9 | - | - | - | - | - |
2 | 0 | 8 | 9 | 93 | 41 | 2 | - | [S-Y] |
3 | 0 | 8 | 9 | 16 | 41 | 2 | 94 | [Y-V] |
4 | 0 | 8 | 9 | 16 | 22 | 2 | 94 | [Y-Z] |
5 | 0 | 8 | 9 | 16 | 22 | 2 | 38 | [V-X] |
6 | 0 | 8 | 9 | 16 | 22 | 2 | 38 | [Z-W] |
7 | 0 | 8 | 9 | 16 | 22 | 2 | 38 | [X-T] |
So, the answer is [S-Y],[Y-V],[Y-Z],[V-X],[Z-W],[X-T].
Part C: The edges are considered from smallest to largest, and whenever an edge connects two distinct connected components, it is inserted into the MST:
So, the answer is [Y-V],[Y-Z],[S-Y],[X-V],[Z-W],[X-T].
Each iteration is going to take the first entry off the Dijkstra map and see if the path going through that node leads to shorter distances for every node to which it is connected.
In the first iteration, S is considered, and it puts V, Z and Y on the map:
In the second iteration, Y is considered. It puts U and W onto the map, and updates Z's entry. Here is the answer to part B:
typedef vector <double> VD; class FindA { public: vector <VD> A; double f(int i, int j); } double FindA::f(int i, int j) { double a1, a2; if (i == 0 && j == 0) return A[0][0]; if (j == 0) return A[i][0] + 0.5 * f(i-1, 0); if (i == 0) return A[0][j] + 1.5 * f(0, j); a1 = A[i][j] + 0.5 * f(i-1, j); a2 = A[i][j] + 1.5 * f(i, j-1); if (a2 < a1) a1 = a2; return a1; } |
To add memoization, simply add a two-dimensional cache. If we could assume that all elements of A are non-negative, we can use sentinel values of -1 for the cache:
typedef vector <double> VD; class FindA { public: vector <VD> A; double f(int i, int j); vector <VD> cache; } double FindA::f(int i, int j) { double a1, a2; int k; if (cache.size() == 0) { cache.resize(A.size()); for (k = 0; k < A.size(); k++) cache[k].resize(A[k].size(), -1); } if (cache[i][j] != -1) return cache[i][j]; if (i == 0 && j == 0) { a1 = A[0][0]; } else if (j == 0) { a1 = A[i][0] + 0.5 * f(i-1, 0); } else if (i == 0) { a1 = A[0][j] + 1.5 * f(0, j); } else { a1 = A[i][j] + 0.5 * f(i-1, j); a2 = A[i][j] + 1.5 * f(i, j-1); if (a2 < a1) a1 = a2; } cache[i][j] = a1; return a1; } |
If we didn't want to make that assumption, there are a few alternatives. First, we could calculate an appropriate sentinel, like 1 + ( 1.5 * sum of the absolute values of all A[i][j]). Or we could have separate array of integers that say whether a cache entry has been calculated yet. Here's the first code:
typedef vector <double> VD; class FindA { public: vector <VD> A; double f(int i, int j); vector <VD> cache; double sentinel; } double FindA::f(int i, int j) { double a1, a2; int k, l; if (cache.size() == 0) { sentinel = 1; for (k = 0; k < A.size(); k++) { for (l = 0; l < A[k].size(); l++) a1 = A[l][k]; if (a1 < 0) { sentinel += ( -a1 * 1.5 ); } else { sentinel += a1 * 1.5 ; } } cache.resize(A.size()); for (k = 0; k < A.size(); k++) cache[k].resize(A[k].size(), sentinel); } if (cache[i][j] != sentinel) return cache[i][j]; if (i == 0 && j == 0) { a1 = A[0][0]; } else if (j == 0) { a1 = A[i][0] + 0.5 * f(i-1, 0); } else if (i == 0) { a1 = A[0][j] + 1.5 * f(0, j); } else { a1 = A[i][j] + 0.5 * f(i-1, j); a2 = A[i][j] + 1.5 * f(i, j-1); if (a2 < a1) a1 = a2; } cache[i][j] = a1; return a1; } |
Here's the second:
typedef vector <double> VD; typedef vector <int> VI; class FindA { public: vector <VD> A; double f(int i, int j); vector <VD> cache; vector <VI> cachevalid; } double FindA::f(int i, int j) { double a1, a2; int k; if (cache.size() == 0) { cache.resize(A.size()); for (k = 0; k < A.size(); k++) cache[k].resize(A[k].size()); cachevalid.resize(A.size()); for (k = 0; k < A.size(); k++) cachevalid[k].resize(A[k].size(), 0); } if (cachevalid[i][j]) return cache[i][j]; if (i == 0 && j == 0) { a1 = A[0][0]; } else if (j == 0) { a1 = A[i][0] + 0.5 * f(i-1, 0); } else if (i == 0) { a1 = A[0][j] + 1.5 * f(0, j); } else { a1 = A[i][j] + 0.5 * f(i-1, j); a2 = A[i][j] + 1.5 * f(i, j-1); if (a2 < a1) a1 = a2; } cachevalid[i][j] = 1; cache[i][j] = a1; return a1; } |