CS302 2022 Final Exam Answers and Grading


Questions 1 - 9

These were presented in random order. I'll incude the questions here so you can find yours.

Grading

2 points per question.


Question 10

These came from a bank. I'm going to answer the one in Exam.pdf. Let's process. We start with node B, whose distance is 16. Here are paths going through node B:
  C   D   E   F   G   H   I   J
 24  42  45   -   -  46  35 114
So we:

Add D-42
Add E-45
Add J-114
Change C-24
Change H-46
Change I-35

Now node C with a distance of 24:

   D   E   F   G   H   I   J
   -   -   -   -  70  48   -
That adds and changes nothing. Now G with a distance of 28:
   D   E   F   H   I   J
  49  81   -  35   -   -
That changes H:

Change H-35

Now I with a distance of 35:

   D   E   F   H   J
   -   -  48   - 132
So that adds F:

Add F-48

That's four adds and four changes, so we're done.

Grading

To help with partial credit, I divided the grading into six parts: The details of your answers are part of the grading information sent to you, broken out into its own place. Look for it. The "answer key" is the non-zero weights on your adjacency matrix for node A, concatenated into a string. So, for example, if your adjacency matrix was:
       A   B   C   D   E   F   G   H   I   J
     --- --- --- --- --- --- --- --- --- ---
A |   --  16  60  --  --  --  28  82  65  --
B |   78  --   8  26  29  --  --  30  19  98
C |   --  50  --  --  --  --  --  41  24  --
D |   31  54  12  --  49  96  78  --  44  --
E |   --   5  62  16  --  88  47  --  --  --
F |   59   1  --  --  --  --  38  --  --  91
G |   10  74  20  21  63  --  --   7  --  --
H |   16  --  34  -- 100  97  15  --  --  --
I |   --  74  42  --  --  13  --  --  --  87
J |   48  --  --   2  --  --  82  43  70  --
Then your key is "1660288265".


Question 11

These came from a bank. I'm going to answer the one in Exam.pdf. I'll include the question in italics: You are sorting the following string using quicksort, with median-of-three pivot selection: FBRRBABABBB. In the very first partition, what is the pivot? So, the answer is: B.
You are sorting the following string using quicksort:

MZYTWQIAELBOK

Suppose you use the first character, 'M', as a pivot. Please show what the string looks like after you have done the first partition, but before you swap 'M' into place.


You are sorting the following string using quicksort, using the first character, 'H', as the pivot:

HBHKHCBCBUHYC

Show what the string is after you partition, before you swap 'H' into place.


You are sorting the following string using mergesort:

FXRIJCAKUWSZ

Please enter what the string is before you perform the final merge.

Grading

4 points per part. I have the answers specific to your questions in the grading file that I sent you, so that you can see the correct answers.


Question 12

These came from a bank. I'll answer the one in Exam.pdf. There is a typo in that PDF (which I fixed before the exam) -- it has two part 3's -- just fix it mentally.

Part 1: The answer was the same on all questions -- the shortest path from S to T is SCFT.

Part 2: Edge EF is the limiting one -- the flow is 18.

Part 3: This is the edge SC, whose weight is 66-18 = 48.

Part 4: The edge FT.

Part 5: Its new weight is 53-18 = 35.

Part 6: These are the reverse edges: CS, FC and TF.

Part 7: Their flow is 18.

Part 8: This isn't hard to eyeball, since CF, DF and ET are so much smaller than the others. It should be clear that they make a cut, so their sum is the maximum flow: 18+12+19 = 49.

Part 9: As specified above: CF, DF and ET.

Grading

You should be able to figure out how your problem maps to the one above to check your grade.


Question 13

Again from a bank. Here's the one in Exam.pdf: We have an undirected, weighted graph with eight nodes, A through H. I specify it below by listing the edges and their weights. There are four columns, and each column has the edge and then its weight.

GH 10     FG 34     AE 50     CG 73
BC 16     CD 35     AD 56     DE 84
FH 21     DG 40     CE 59     EG 89
CH 23     AF 41     BE 65     AG 92
AC 24     AH 43     CF 69     DH 95
BF 32     EH 47     DF 70     AB 98
BD 33     BH 49     BG 71     EF 99

Please enter the minimum spanning tree of this graph. List the edges separated by spaces. Best to go through Kruskal's algorithm, since the edges are sorted. I'll show the disjoint sets to the right

          ABCDEFGH
GH        ......xx
BC        .yy...xx
FH        .yy..xxx
CH        .xx..xxx
AC        xxx..xxx
BF        xxx..xxx  Ignore
BD        xxxx.xxx
FG        xxxx.xxx  Ignore
CD        xxxx.xxx  Ignore
DG        xxxx.xxx  Ignore
AF        xxxx.xxx  Ignore
AH        xxxx.xxx  Ignore
EH        xxxx.xxx

The answer is:



GH BC FH CH AC BD EH

Grading

You'll see your grading as something like:

AE-AF-DG-AD-AH-BH-BC-matches-6-misses-1

The initial part is the minimum spanning tree for your problem, and then how many edges you had right and wrong. Grades were:


Question 14

Here's a commented answer, in 14-answer.cpp

#include <string>
#include <vector>
#include <list>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
using namespace std;

/* I just made this up so I can test. */

double cost(const string &s1, const string &s2, int n)
{
  size_t i;
  double j;
  double val;

  val = 0;
  for (i = 0; i < s1.size(); i++) val += s1[i];
  for (i = 0; i < s2.size(); i++) {
    j = s2[i];
    j /= 26.0;
    val += j;
  }
  val += ((n%7)*(n%7));
  return val;
}

/* Global variable is poor form, but this is an exam and I said it was ok. */

map <string, double> Cache;

double mincost(const string &x, const string &y, int z)
{
  string key;
  char sz[20];
  bool isset;
  double rv, rec;
  /* Base case */

  if (z == 0 && x.size() == 0 && y.size() == 0) return 0;

  /* Make the cache string and check the cache. You need a delimeter between 
     x and y, otherwise you can't differentiate (x = "a", y = "bc") from 
     (x = "ab", y = "c"). */

  key = x;
  key += ":";    // You can use any delimiter that is not a lowercase letter.
  key += y;
  key += ":";    // This is unnecessary, but I'd include it to help debug.
  sprintf(sz, "%d", z);
  key += sz;
  if (Cache.find(key) != Cache.end()) return Cache[key];

  /* If it's not in the cache, make the recursive calls, but don't add the cost in yet. */
  
  isset = false;
  if (x.size() > 0) {
    rv = mincost(x.substr(0, x.size()-1), y, z);
    rec = mincost(x.substr(1, x.size()-1), y, z);
    if (rec < rv) rv = rec;
    isset = true;
  }
  if (y.size() > 0) {
    rec = mincost(x, y.substr(1, y.size()-1), z);
    if (!isset || rec < rv) rv = rec; 
    isset = true;
  }

  if (z > 0) {
    rec = mincost(x, y, z-1);
    if (!isset || rec < rv) rv = rec; 
  }
  
  /* Add the cost at the end. */

  rv += cost(x, y, z);

  /* Set the cache and return. */

  Cache[key] = rv;
  return rv;
}

int main()
{
  string x, y;
  int z;

  if (!(cin >> x >> y >> z)) exit(0);

  cout << mincost(x, y, z) << endl;
  return 0;
}


Grading

This was a straightforward program, but many of you got tripped up with small things. Had everyone been strapped for time, I'd be more lenient with the grading, but pretty much everyone ended early, so you had the time to get the details correct.

You started with 16 points, and were deducted for things that were wrong. Common deductions:

  • Key never created: -5
  • Linearly searching a map: -5 -- You should know never to do that.
  • No type specifications in mincost(): -3
  • Setting incorrect values in the cache (usually by setting them inside the if statements): -2
  • Cache never used: -2
  • cost() is only called in the main(): -2
  • No main(): -2
  • Z is not in the key: -2
  • Using uninitialized variable(s) when if statements are false: -2. A lot of you did this.
  • Wrong base case (some of you liked to ignore z): -2
  • Using argc/argv instead of standard input: -1 -- please read the instructions...
  • Multiple calls to cost() -- you only needed to call cost() once inside of mincost(). Who knows how expensive it is, so you should only call it once: -1
  • Strings should be reference parameters. No need to make extra copies of memory when you don't need them. -1
  • No delimiter in the memoization key -- you need somethinge between x and y in the key. Otherwise, you can't differentiate x = "", y = "a" from x = "a", y = "".
  • Using a char limits z to a max of 127. Some of you did something like key = x + " " + b + (char) z. That compiles and runs, but limits z.
  • Fixed size buffer limits the size of x and y. If you're using sprintf, you'd do better to just use it for z, where you know the max size.
  • Uses a sentinel. I didn't specify what cost() returns, so you can't use any value as a sentinel. There are better ways to handle it, like I did above. A bunch of students used sets to hold the answer and then return the first one on the set -- that was very nice!

    Question 15

    The first question: Your cache can have all substrings of x, all suffixes of y, and all values from 0 to z. So the answer is O(X2YZ). If you set up your cache incorrectly on the previous question, then you are likely to have gotten this question wrong.


    The second question: shortest path on a directed, acyclic graph (since we deleted the roads that go south or east). So, this is topological sort. O(C+R).