Answers are in italics.
1. HashTable<int,bool> visited(0); 2. HashTable<int,int> M(0); 3. 4. int mystery(Digraph G, int vid) { 5. if (! visited[vid]) { 6. visited[vid] = 1; 7. M[vid] = vid; 8. Array<int> out_nbrs = G.out_nbrs(vid); 9. for (int i = 0; i < out_nbrs.size(); ++i) { 10. int P = mystery(G, out_nbrs[i]); 11. if (P > M[vid]) M[vid] = P; 12. } 13. } 14. return M[vid]; 15.}
Here is an example graph, too:
1A. Run mystery(G, 3) on the digraph G above. What are the values of (M[1], M[2], ..., M[9]) when the algorithm finishes.
I first wrote: 8, 8, 7, 8, 6, 6, 7, 8, but this is incorrect. This is what the values would have been after mystery(G, 1). The correct answer is: 0,0,7,0,0,6,7,0. (I think!)
1B. Suppose G is ANY digraph without cycles. Describe, in words, what mystery(G, s) returns. (For example "mystery(G,s) counts the number of nodes reachable from vertex s" except that is the wrong answer.)
The largest id of any vertex reachable from s.
1C. Over all input graphs with N vertices and M edges, what is the worst-case running time of mystery(G,v)? (Use big-O notation to give your answer.) Explain, carefully, why.
O(N+M). The running time is the same as DFS. More specifically, the algorithm does work for each vertex proportional to the degree of the vertex, so the total work is proportional to the number of vertices plus the number of edges.
1D. Carefully explain your reasoning that makes you believe your answer to 1B is correct. (If you are not sure that it is correct, be frank about what your doubts are.)
Define MV[v] = maximum id of any vertex reachable from v.
Claim: MV satisfies the following recurrence:
This is because the set of vertices reachable from v is v, together with the union of the sets of vertices reachable from the out-neighbors of v.
By looking at the algorithm, we can see that it calculates M[v] according to this recurrence. Therefore M[v] as calculated by the algorithm equals MV[v], the maximum id of any vertex reachable from v.
1. HashTable<int,bool> visited(0); 2. HashTable<int,int> S(0); 3. 4. int mystery2(Digraph G, int vid, int vid2) { 5. if (! visited[vid]) { 6. visited[vid] = 1; 7. if (vid == vid2) S[vid] = 1; // this line changed 8. Array<int> out_nbrs = G.out_nbrs(vid); 9. for (int i = 0; i < out_nbrs.size(); ++i) { 10. int P = mystery2(G, out_nbrs[i], vid2); 11. S[vid] += P; // this line changed 10. } 11. } 10. return S[vid]; 11.}
2A. Run mystery2(G, 1, 6) on the example graph from problem 1. What are the values S[1], S[2], ..., S[9] when the algorithm finishes?
I first wrote 2,1,1,0,0,1,0,0, but this is wrong. The correct answer is 3,2,1,0,0,1,0,0 (I think!).
2B. In words, if G is ANY directed acyclic graph, what does mystery2(G, s, t) return?
The number of paths from s to t.
3A. Find the longest ascending subsequence within this sequence:
0,1,6,8,16
3B. Suppose A is an array of N integers. For i=0,1,...,N-1, define LAS[i] to be the length of the longest ascending subsequence within (A[0],A[1],A[2],...,A[i]) that ends with A[i] .
For example, for the sequence S, above, we have
1,3,3,1,2,2,1,3,3,4,4,5,4
3C. Describe a a recurrence relation between LAS[i] and the values (LAS[0], LAS[1], ..., LAS[i-1]) and the values (A[0], A[1], ..., A[i]). (one that holds for any sequence of N integers, where N can be any positive integer). Hint: your rule should be implementable in O(i) time, once the values (LAS[0], LAS[1], ..., LAS[i-1]) have been computed.
Another hint: a longest ascending subsequence ending at A[i], if you remove A[i], gives either the empty sequence, or it gives a longest ascending subsequence ending at some A[j] for j < i and A[j] < A[i].
LAS[i] = max { 1, 1 + max { LAS[j] : 1 ≤ j < i and A[j] < A[i] }
3D. Describe an efficient algorithm for computing the length of the longest ascending subsequence, given a sequence (A[0], A[1], A[2], ..., A[N]). Use pseudo-code to describe your algorithm. What is the running time of your algorithm?
1. For i = 1 .. n do
2. LAS[i] = max { 1, 1 + max { LAS[j] : 1 ≤ j < i and A[j] < A[i] }
3. return max { LAS[i] : 1 ≤ i ≤ n } .
The running time is O(n2). Line 2 can be computed in O(n) time.
3E. Prove that your recurrence relation from 3C is correct.
The correctness of the recurrence follows from the following claim:
The longest ascending subsequence ending at (and including) A[i] is either {A[i]} or it is a longest ascending subsequence ending at A[j] (where j < i and A[j] < A[i]), followed by A[i].
This claim may be obvious, but in case it is not we give a line of reasoning that is broken down into even more obvious steps.
First, for any j such that j < i and A[j] < A[i], the longest ascending subsequence ending at A[j], followed by A[i], is an ascending subsequence ending at A[i]. Thus, LAS[i] ≥ 1+LAS[j] for any such j. Thus, LAS[i] ≥ 1+max { LAS[j] : j < i and A[j] < A[i].
Clearly LAS[i] ≥ 1.
Combining the last two paragraphs, we get
---
To finish we need only convince ourselves that
Let S be a longest ascending subsequence ending (and including) A[i], so that the length of S is LAS[i].
Let S' be the subsequence S without the final term A[i]. If S' is empty, then LAS[i] = 1.
Otherwise, S' ends in some A[j] with j < i. Since S was an ascending subsequence, it must also be that A[j] < A[i]. Also, it must be that S' is an ascending subsequence. Thus, LAS[j] ≥ length(S') = LAS[i] - 1.
From the previous two paragraphs we can conclude that either LAS[i] = 1, or there exists a j with j < i and A[j] < A[i] such that LAS[i] ≤ LAS[j] + 1. Thus,