CS302 Final Exam - Answers and Grading

James S. Plank - December 11, 2018


Question 1

Grading

15 points: 2, 3, 3, 3, 2, 2. See above for partial credit.


Question 2

  1. P does more than return "halt." It also returns "infinite loop" if the program goes into an infinite loop.
  2. A very important part of the halting problem is that P has to return in finite time. For that reason, it does not "run the program." Instead, it determines its answer in a finite period of time.
  3. We know whether it's possible to write P: It is impossible.
Grading: Four points for getting one of the three. 10 points for getting two of the three. 12 for getting all three. If you didn't get two, but you tried to tell me the proof, you got two more points. All I cared about was stating the problem, not proving it.


Question 3

When you're answering a question like this, try to think about why I'm specifying the different graphs:

Given that:

Grading: 2 points per part. Here's my partial credit table (the non-listed answers were always zero:

    O(1)  O(V)  O(E)  O(V+E)  O(Vlog(V))  O(Elog(E))
A.  0.0   0.0   0.0    0.0       2.0         1.5
B.  2.0   0.5   2.0    1.5       0.0         0.0
C.  0.0   0.5   1.5    2.0       0.5         1.0
D.  0.0   2.0   2.0    1.5       1.0         0.5
E.  0.0   0.0   0.0    0.0       1.0         2.0
F.  0.0   0.0   0.0    0.0       2.0         1.5
G.  0.0   0.5   1.5    2.0       0.0         0.0
H.  0.0   0.5   2.0    1.5       0.0         0.0
I.  0.0   2.0   2.0    1.5       1.0         0.5
J.  0.0   0.0   0.0    0.0       1.0         2.0 


Question 4

Here is the graph for parts A through C:

Part A:

Print A.
A calls DFS on D.
  Print D.
  D calls DFS on C.
    Print C.
    C calls DFS on B.
      Print B.
      B calls DFS on E.
         Print E.
        E calls DFS on C, and C is already visited.
        E returns.
      B returns.
    C returns.
  D calls DFS on E, and E is already visited.
  D returns.
A calls DFS on F.
  Print F.
  F calls DFS on C, and C is already visited.
  F calls DFS on D, and D is already visited.
  F returns.
A returns.
The answer is: A, D, C, B, E, F. Grading: 3 points. If you gave me a valid DFS, ignoring the order of the adjacency lists, you got 1.5 points.

Part B:

Action                                                                  Queue
-----------------------------------------------------------------------------
A is put on the queue                                                   { A }
Pull A off the queuei, and print A.                                     { }
Push D and F on the queue                                               { D, F }
Pull D off the queue, and print D.                                      { F }
Push C and E on the queue                                               { F, C, E }
Pull F off the queue, and print F.                                      { C, E }
Push D on the queue (don't have to push C, because it's already there)  { C, E, D }
Pull C off the queue, and print C.                                      { E }
Push B on the queue                                                     { E, B }
Pull E off the queue, and print E.                                      { B }
Don't push C, because it has been processed already.                    { B }
Pull B off the queue, and print B.                                      { }
Don't push E, because it has been processed already.                    { }
Done.
The answer is: A, D, F, C, E, B. Grading 3 points, with partial credit to valid BFS's if you get the order of the adjacency list wong.

Part C: Start with labeling each node with its number of incident edges:

A B C D E F
0 1 3 2 1 1  (remember, there is no edge BE now)
You will always print a node with zero incident edges, and then decrement the incident edges for each edge on the node's adjacency list:
Action                                    Incident Edges: A B C D E F
----------------------------------------------------------------------
Start                                                     0 1 3 2 1 1
Print A and decrement incident edges for D and F          - 1 3 1 1 0
Print F and decrement incident edges for C and D          - 1 2 0 1 -
Print D and decrement incident edges for C and E          - 1 1 - 0 -
Print E and decrement incident edges for C                - 1 0 - - -
Print C and decrement incident edges for B                - 0 - - - -
Print B                                                   - - - - - -
The answer is: A, F, D, E, C, B.

Grading is 3 points. You got partial credit for starting with AF.

Here is the graph for parts D through F.

The best thing to do here is simply to process Dijkstra's algorithm:

Visit: Best Paths: 0  1  2  3  4  5  Visited: 0 1 2 3 4 5   Multimap:
-------------------------------------------------------------------
Start              0  -  -  -  -  -           N N N N N N   {0,0}
0                  0 15  9  -  - 28           Y N N N N N   {9,2},  {15,1}, {28,5}
2                  0 10  9 17 19 28           Y N Y N N N   {10,1}, {17,3}, {19,4} {28,5}
1                  0 10  9 17 13 28           Y Y Y N N N   {13,4}, {17,3}, {28,5}
4                  0 10  9 17 13 25           Y Y Y N Y N   {17,3}, {25,5}
3                  0 10  9 17 13 24           Y Y Y Y Y N   {24,5}
5                  0 10  9 17 13 24           Y Y Y Y Y Y   
Part D: 24.
- 3 points. Part E: 0, 2, 1, 4, 3, 5: Four points, with partial credit for answers that started with 0 and 2.

Part F The easiest thing here is simply to do Kruskals algorithm, going through the edges in increasing order of weight:

Edge                         Components         Action
----------------------------------------------------------
Start                        0 1 2 3 4 5
1-2                          0 12  3 4 5        Add to MST
1-4                          0 124 3   5        Add to MST
1-3                          0 1234    5        Add to MST
3-5                          0 12345            Add to MST
2-3 - Same component.        0 12345            Ignore
0-2                          012345             Add to MST
Done.
----------------------------------------------------------
The answer is: 1-2, 1-4, 1-3, 3-5, and 0-2. You can specify them in any order. Grading is 0.6 per correct edge.

Here is the graph for parts G through J.

Part G: There are only three paths through the graph with flow:

So the answer is 0-1-2-3-5.

Grading was 3 points, with 1 point to paths with flow that weren't maximal.

Part H: The minimum hop path is 0-1-3-5, with a flow of 5. So, the residual graph becomes (with changes in red):

  |  0  1  2  3  4  5 |
--| -- -- -- -- -- -- |
0 |  - 25  -  -  -  - |
1 |  5  - 22  -  -  - |
2 | 12  -  - 13  8  - |
3 |  -  5  -  -  - 40 |
4 |  -  -  - 22  -  - |
5 |  -  -  -  5 17  - |
--| -- -- -- -- -- -- |
Grading: 0.7 per correct edge/weight combo. 0.3 per correct edge/weight combo if you chose the path as 0-1-2-3-5 with a flow of 13, and 0.2 per correct edge/weight combo if you chose the path as 0-1-2-4-3-5 with a flow of 8.

Part I: As mentioned above, the cut is composed of edges (1-3), (2-3) and (2-4). Grading: Each of those was worth 1.2 points.

Part J: While you can go through the flow calculation for this formally, it's really easier to eyeball to see that edges (1-3), (2-3) and (2-4) compose the minimum cut. However, if you do go through it formally, you can process the paths above, and there will not be any more paths to process with residual edges:

The answer is 5+13+8 = 26. Grading: 3.2 points. There was some partial credit for 19, 25, 27, 18 and 13.

Grading


Question 5

Part A: Simply clear the cache and call DP(0, B, T):
  Cache.clear();
  return DP(0, B, T);
Some of you took the max of DP(i, B, T) for i going from 0 to N-1. That works too, although it's a little more inefficient. I gave that answer full credit:

Grading: 3 points for the DP() call. 1 for clearing the cache.

Part B: The arguments to DP() seem straightforward. I'd use sprintf():

   sprintf(buf, "%d %d %d", index, b, t);
   key = buf;
Grading: 2 points for the key, and 2 points for the code.

Part C: Again, simply reason through the arguments to get the base cases:

Part D: You're going to make two recursive calls:

  1. The first is when you don't include the player at index on the team:
    DP(index+1, b, t);
    
  2. The second is when you do include the player at index on the team:
    DP(index+1, b-C[index], t-1);
    
You got four points for getting one of these, and two more for getting the other. If you had a loop that did the second call for all values of i from index+1 to N, you got full credit, so long as the rest of the details were correct.

Part E: It is the number of potential memoization strings (yes, it's less than that, but in terms of big-O, it will be that): O(N*B*T). Y'all didn't do well on this, answering N! and 2N. The former is the number of permutations of N elements, and the second is the number of subsets of a set of N elements. Neither is the case here. You simply have N possible values of index, B values of b, and T values of t.