General notes on marking to TAs: Solutions that demonstrate the "right idea" should be worth at least half of the total marks for the given question. Incorrect solutions that demonstrate a lack of understanding shouldn't be awarded more than half the total marks for the given question. If there is a situation that arises that isn't discussed in the solutions below, feel free to deduct marks as you see appropriate. In this case, please either note the reason for the deduction on the students paper, or make use of "marking codes" (i.e., use a short "code" on the student's paper and keep track of a list of your codes and meanings so this information can be posted to the discussion board later). 1. No, it's not possible to determine whether Foo is a Stack or a Queue. (1 mark) The reason is because what's printed to screen is the same regardless of whether Foo is a Stack or a Queue. (3 marks) Modifying the code snippet properly is worth 2 marks. To get both marks, the change should be no more complicated than necessary. In particular, the change should either involve removing a line, modifying a single line, adding a single line, or swapping two lines. If the change is correct but overly complicated, award only 1 mark. An incorrect modification should be awarded 0 marks. One possible solution: The first f.foo_add(1) can be changed to f.foo_add(3). There are many other possibilities. 2. Defining StackEmptyException correctly is worth 3 marks: class StackEmptyException(Exception): pass or class StackEmptyException(Exception): def __str__(self): return "tried popping from empty stack" # or some other string If the student has the following declaration: class StackEmptyException(Exception): (i.e., without "pass") then deduct 1 mark. If the student has the following declaration: class StackEmptyException: (i.e., did not make the class a subclass of Exception) deduct 2 marks. Rewriting pop is worth 5 marks: def pop(self): if self.is_empty(): # 3 marks for checking for empty stack raise StackEmptyException() # and raising the exception. return self.stack.pop() # 2 marks for popping if exception is not raised. If the student tries wrapping call to self.stack.pop() in try-except block and then raising StackEmptyException in the except block, then it's possible that the exception is raised in response to a situation in which the stack is not empty. Deduct 2 marks in this situation. 3. F / \ / \ M A / \ \ K R P \ Q The right subtree can also be: A \ P / Q or A / P / Q or A / P \ Q - Award 2 marks for placing F in the correct position in the tree - Award 2 marks for placing M in the correct position in the tree - Award 2 marks for placing A in the correct position in the tree - Award 1 mark for each other node in its correct position in the tree 4. If the statement is True, award 2 marks for circling True, 0 marks for circling False. If the statement is False, award 1 mark for circling False, and 1 mark for a reasonable explanation. a) False. The BST may be skewed to one side. Searching such a BST is no better than searching a list in the worst case. b) True c) False. (This statement is nonsense. Any reasonable explanation mentioning the differences between the Queue ADT and the Priority should be awarded full marks.) d) True. 5. This is a simple variant of the regular towers of hanoi problem. def mirrored_hanoi(n, frompeg, topeg, withpeg): if n >= 2: mirrored_hanoi(n-2,frompeg, withpeg, topeg) print "Move pair of discs from "+frompeg+" to "+topeg mirrored_hanoi(n-2,withpeg, topeg, frompeg) If the terminating condition is missing, deduct 3 marks. If users try coming up with a totally different solution, then award at least 5 marks if it seems like it will roughly work, but is ultimately incorrect. If solution is completely incorrect, but there is a terminating condition, award 1 mark. Completely incorrect solutions with no terminating conditions are worth 0 marks. 6. The following is one possible (very naive) solution: def make_change(denoms, total): results = [] helper(denoms, total, [0]*len(denoms), results) return results def helper(denoms, total, cur, results): curtot = 0 for i in range(len(denoms)): curtot += cur[i]*denoms[i] if curtot == total: if cur not in results: results.append(cur) if curtot > total: return for i in range(len(denoms)): next = cur[:] next[i] = next[i] + 1 helper(denoms, total, next, results) I'm not concerned about efficiency or how "clever" the solution is, as long as the solution is correct and the list of results does not contain duplicates. If the solution is correct, except the returned result contains duplicate values, then deduct 4 marks. If the solution never terminates under any circumstances, but the solution otherwise appears to be taking the correct approach, deduct 6 marks. If the solution sometimes doesn't terminate, but is otherwise correct, deduct 3 marks. A solution that has the right idea but is ultimately incorrect should be worth at least 6 marks. A completely incorrect solution that at least has a terminating condition should be awarded 1 mark. A completely incorrect solution that doesn't terminate is worth 0 marks.