CS302 Midterm Exam -- 3/5/2026 -- Answers and Grading

James S. Plank

Often questions came from a bank -- when that happens, I show the answer to the example exam, and provide grading information for the banks.


Questions 3-16

These came from a bank, and were presented in random order. You can use the hash key at the beginning of the question as an identifier. This is presented in the same order as the example exam (and your graded exams).


Questions 17-19

Question 17

Answering with respect to the example exam (hash key f7527). When I do Union(2,66), the set headed by 2 is bigger than the set headed by 66, so 2 becomes the parent:
A: links[2] stays at -1.
B: links[66] becomes 2.
C: sizes[2] adds 24 to 33: 57.
D: sizes[66] stays the same: 24.
Answers to all banks:

key:     A     B     C    D
----    --    --    --   --
0158a   -1    43    41   11
08d3b   -1    12    57   28
371c0   56    -1    17   44
f7527   -1     2    57   24

Question 18

Answering with respect to the example exam (hash key 76112). When I do Union(0,4), the set headed by 4 has a greater height than the set headed by 0, so 4 becomes the parent:
A: links[0] becomes 4.
B: links[4] stays at -1.
C: heights[0] stays the same: 4.
D: heights[4] also stays the same: 5.
Answers to all banks:

key:     A     B     C    D
----    --    --    --   --
76110    4    -1     4    5
8a751   -1    26     5    4
defb4   24    -1     4    5
8155a   -1     4     5    4

Question 19

Answering with respect to the example exam (hash key 8df0f). When I do Find(10), the links of 10 and 16 are both set to 44. Everything else remains the same So:
A: links[4]  = 10
B: links[10] = 44
C: links[16] = 44
D: links[50] = -1
E: links[52] = 33
F: links[67] = 21
Answers to all banks:

key:     A     B     C     D     E     F
----    --    --    --    --    --    --
2ee9f   56    39    22    10    22    -1
8df0f   10    44    44    -1    33    21
ed5a1   31    42    -1    38    56    42
fe763   34    22    -1    34     6    38


Question 20

Here are answers to the example exam:

Part A: There are 265 ways to have 5 lower-case letters, and 104 ways to have four single numeric digits. Therefore, the answer is: (265)(104).

Part B: This is equal to the number of 6 digit numbers in base 17: (176).

Part C: 17 choose 5, plain and simple: C(17,5) or C(17,12).

Part D: This is equal to the number of permutations of the colors: (12!).

Part E: This is the power set of the juices: (212).

Here are answers to the problems specified by their grading keys (which will be in your exam):

abstract-art-C(14,6) or abstract-art-C(14,8)
band-8!
counters-(7^4)*(2^10)
employee-bonus-2^18
etsy-(2^5)*(3^4)
eyors-5^9
fantasy-9!
flash-cards-9!
gamecard-C(21,8)
garage-C(18,7)
infboxes-6^7
intensity-(2^8)*8!
leds-on-roof-16!
mouse-obstacles-(2^8)*(8!)
office-windows-C(60,40)
pool-chairs-2^25
remote-2^12
sawthee-2^32
shooting-gallery-2^30
thirtovia-13^6


Question 21 = 10 points

Since there are only 31 elements in the set, you can represent each subset with an integer, using bit i to represent whether element i is in the set:

int exactly_n(const vector <int> &sets, int n)
{
  int i, j, num, rv;

  rv = 0;
  for (j = 0; j <= 30; j++) {                 // For each element j
    num = 0;
    for (i = 0; i < sets.size(); i++) {       // For each set i
      if (sets[i] & (1 << j)) num++;          // If element j is in set i, increment num
    }
    if (num == n) rv |= (1 << j);             // If j is in n sets, add it to the return value.
  }
  return rv;
}

Grading -- 10 points -- as always, I look for a few things -- correct structure, correct loops, correct use of bit arithmetic. If your grade says "see the answer", it means that you were pretty far off.


Question 22 - 12 points

You'll note that you want to return X+1, since X is the highest value where f(X) is false.

This is a binary search. As always, I recommend using start and size to define a region of the numbers where the answer is always in the region. When you get to size ==1 , then the answer is start.

Here's the algorithm in English:

- Set start to 0 and size to 2^50+1 (this is because the answer can be 2^50).
- While size > 1 do the following:
  - Set mid to be start + size/2 - 1.  This will be the last element in the first half of the region.
  - if (fmid) is true, then the answer has to be in the lower half -- set size to size/2.
  - if (fmid) is false, then the answer has to be in the upper half, increment start by size/2 and decrement size by size/2.
- At the end, return start.
Here's code, which follows the description above:

int main();
{
  long long start, size, mid;

  start = 0;
  size = (1LL << 50) + 1;

  while (size > 1) {
    mid = start + size/2 - 1;
    if (f(mid)) {
      size /= 2;
    } else {
      start += size/2;
      size -= size/2;
    }
  }
  printf("%lld\n", start);
}

Grading: This was 12 points. If you gave me something that looked like a binary search (code, not English), then you got 6 points. The remaining 6 points were for varying levels of correctness. If I really didn't understand your binary search, I compiled it, and I'm happy to say that there were quite a few solutions that worked when they were compiled (and of course I fixed typos).

There were a few common stumbling blocks:


Question 23 - 12 points

This is a straightforward recursion, just like the Sudoku Solver. The form of the recursion is a follows: Here's the code:

#include <iostream>
using namespace std;

bool can_i_solve(string &s)
{
  int i;
  int count;

  count = 0;

  /* Count the tees, and if there is only one, you're done -- return true. */

  for (i = 0; i < s.size(); i++) if (s[i] == '1') count++;
  if (count == 1) return true;

  /* Run through all of the tees except for the last two.  You'll look at tees
     i, i+1 and i+2.  If you can make a jump with these three tees, then do so
     and make the recursive call.  Then, if the recursive call failed, undo the
     jump. */

  for (i = 0; i < s.size()-2; i++) {

    /* We first look at situations when there are tees in holes i and i+1, but
       no tee in hole i+2: */

    if (s[i] == '1' && s[i+1] == '1' && s[i+2] == '0') {
      s[i] = '0';
      s[i+1] = '0';
      s[i+2] = '1';
      if (can_i_solve(s)) return true;
      s[i] = '1';
      s[i+1] = '1';
      s[i+2] = '0';
    }

    /* We next look at situations when there are tees in holes i+1 and i+2, but
       no tee in hole i: */

    if (s[i] == '0' && s[i+1] == '1' && s[i+2] == '1') {
      s[i] = '1';
      s[i+1] = '0';
      s[i+2] = '0';
      if (can_i_solve(s)) return true;
      s[i] = '0';
      s[i+1] = '1';
      s[i+2] = '1';
    }
  }

  /* If we get here, then we've failed, so return false. */

  return false;
}
 
/* Here's testing code -- obviously, you didn't need this, but if you want to
   test my code, then you can copy-paste all of this. */

int main()
{
  string s;

  cin >> s;
  cout << can_i_solve(s) << endl;
  return 0;
} 

Grading: As with the others -- if you had a basic recursive program with the right structure, you got 6 points. The rest of the points regarded the details. Common problems: