Sei sulla pagina 1di 13

Final Exam, CS161, Fall 2011

1. One application of combinatorics is to gure out whether the number of cases an algorithm has to examine can be handled in a reasonable amount of time. This can save a lot of resources otherwise spent on a wasted eort. Lets use it to get an upper bound for our brute-force Tic-Tac-Toe algorithm: How many games of Tic-Tac-Toe are possible if the players keep taking turns until the board is full, even if somebody has won? Two games are considered dierent unless the exact sequence of moves is the same. Solution: There are nine choices for the rst move, eight for the second, seven for the third, etc. 9! ways. 2. It has been reported that chickens can be taught to play competent tic-tac-toe. YouTube features some examples that are obvious hoaxes on close inspection. A circus that wants to have the real thing has called you in as a consultant. They want to nd out whether the following strategy is a possibility: could an animal trainer teach the chicken, for each possible board conguration, a best next move? That way, the chicken doesnt need to reason; it can just memorize what to do in each situation. (This is also of interest when considering one strategy for game-playing algorithms: ll out the best move in a table to avoid the large number of repetitions of the same calculations your tic-tac-toe algorithms incurred.) The chickens display has a top and a bottom, and it isnt smart enough to recognize that a conguration is just a reection or rotation of one that it knows about. Get a lower bound on the number of facts you would have to teach the chicken as follows: A minimum number of moves until a win is ve. Therefore, the chicken must at least know about every conguration where each player has moved twice. Calculate the number of congurations there are on boards where white has moved exactly twice and black has moved exactly twice. (White always moves rst.) Solution: C(9, 2) choices of positions for the white pieces and then C(7, 2) for the black pieces. 9 8 7 5/4 = 756.

3. On a TV show called The Mentalist, a detective formerly worked as a successful fake T.V. psychic. Now he uses the skills he learned in his old profession to catch criminals. In one episode, he tells some bad guys that he can pick the rst, second, and third place horse in an upcoming horse race. After the race, he shows them a race receipt that proves that he had successfully picked all three in the correct order. Later, he tells his police colleagues that he had bet fty cents on every possible outcome, and spent a total of more than $5000, which was only partly oset by $500 he got from the one bet he won. (a) Given that there were 14 horses in the race, how much could he have saved if he had placed the bets competently? Solution: P (14, 3) = 14 13 12 bets, at 50 cents apiece is $1092. Considering that he won $500, he could have spent only $592. (b) Write a method to give him a hand next time. Assume the horses are numbered from 1 through n. It should print out all possible bets, using three integers per line, corresponding to the rst-, second-, and third-place winners. void bets(int n) { for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++) if (i != j && i !=k && j !=k) System.out.println(i + " " + j + " " + j); }

4. Show by induction that

n i i=0 r

= (rn+1 1)/(r 1) if r = 1.

(b) State what two expressions are assumed to be equal for the induction hypothesis. Solution: k ri = (rk+1 1)/(r 1) for some k 0. i=0

(a) Show that its true when n = 0. Solution: 0 = r0 = 1 and (r1 1)/(r 1) = 1. i=0

(d) Using algebra, show that they are equal if the inductive hypothesis is true. Solution: k+1 ri = k ri + rk+1 = (rk+1 1)/(r 1) + rk+1 = (rk+1 1 + i=0 i=0 rk+2 rk+1 )/(r 1) = (rk+2 1)/(r 1).

(c) State what expressions you are going to try to show are equal for the induction step. Solution: k+1 ri = (rk+2 1)/(r 1). i=0

5. Write the following method for the ObjList class, below. public Object removeLast() This removes the last node from the list and returns its data element, not the node that contains it. A precondition that the list contains at least one element, so you dont have to check this. I gave this problem on the second midterm. However, almost everybody forgot to consider the case where there was only one element in the list. Write a corrected version. You can see from the implementation of find that if there is only one element in the list, there is only one node in the list. (No dummy node is kept at the front.) /*******************************/ /* Node.java */ /*******************************/ public class Node { private Object item; private Node next; public Node(Object newItem) { item = newItem; next = null; } public Node (Object newItem, Node nextNode) { item = newItem; next = nextNode; } public void setItem(Object newItem) { item = newItem; } public Object getItem() { return item; } public void setNext(Node nextNode) { next = nextNode; } 4

public Node getNext() { return next; } }

/**************************/ /* ObjList.java */ /**************************/ public class ObjList { private Node _head; // head of list private int _size; // number of items in list public ObjList() { setSize(0); setHead(null); } public boolean isEmpty() { return getSize() == 0; } public int getSize() { return _size; } public void setSize(int size) { _size = size; } public Node getHead() { return _head; } public void setHead(Node head) { _head = head; } public Node find (int index) 5

{ Node curr = getHead(); for (int skip = 1; skip < index; skip++) curr = curr.getNext(); return curr; } } Solution: public Object removeLast() { Object data; if (getSize() == 1) { data = getHead().getItem(); setHead(null); setSize(0); } else { Node n = find(getSize()-1); data = n.getNext().getItem(); n.setNext(null); setSize(getSize() - 1); } return data; }

6. Now consider the subclass we created from it. public class ObjListTail extends ObjList { private Node _tail; // pointer to last node of list public ObjListTail() { super(); setTail(null); } public void setTail(Node tail) { _tail = tail; } public Node getTail () { return _tail; } public Object removeLast() { Object data = super.removeLast(); if (getSize() == 0) setTail(null); else setTail(find(getSize())); return data; } } Recall that tail contains a reference to the last node in the list. Add a new method to the ObjListTail class: void append(ObjListTail L) It should add all the elements of L to the end of the list, destroying the object L. Your method must run in constant time. (The running time cant depend on the lengths of the lists.) A precondition is that both lists have at least one element. Note that this problem is similar to the fuse operation of the Group class, except that there is no need to update references to the front of the list. Thats why you can do it in constant time.

Write your method here. void append(ObjListTail L) { getTail().setNext(L.getHead()); setTail(L.getTail()); setSize(getSize() + L.getSize()); L.setHead(null); L.setTail(null); L.setSize(0); } 7. Two hunters get drunk and turn their guns at an 70 cm 70 cm highway sign. They manage to hit the sign fty times. Show that there are two bulletholes that are closer together than 15 cm. Draw a picture to illustrate your argument. Solution: Divide the sign into a 7 7 set of squares, each of size 10 10. There are 49 squares and fty bulletholes. One square must have two bulletholes, and all points in the square are within 10 2 cm. of each other, which is less than 15 cm.

8. Recall the Graph class we wrote: public class Graph { private int _n; private int _m; private AdjList _Adjacencies []; // default constructor public Graph() { setNumVertices(0); setNumEdges(0); _Adjacencies = null; } // constructor that creates an empty graph on n vertrices public Graph(int n) { setNumVertices(n); setNumEdges(0); _Adjacencies = new AdjList[n]; for (int i = 0; i < n; i++) _Adjacencies[i] = new AdjList(); } private void setNumVertices(int n) { _n = n; } public int getNumVertices() { return _n; } public void setNumEdges(int m) { _m = m; } public int getNumEdges() { return _m; } // Get the list of neighbors of vertex i public AdjList getList(int i) 9

{ return _Adjacencies[i]; } // Add an edge betwen vertex i and vertex j public void AddEdge(int i, int j) { if (0 <= i && i < getNumVertices() && 0 <= j && j < getNumVertices()) { getList(i).insertAtFront(j); getList(j).insertAtFront(i); setNumEdges(getNumEdges() + 1); } } // see handout for specifications public String toString() { String Result = getNumVertices() + " " + getNumEdges() + "\n"; for (int i = 0; i < getNumVertices(); i++) Result += i + ": " + getList(i) + "\n"; return Result; } } Here is the implementation of the AdjList class that goes with it: public class AdjList { private AdjListNode _head; // head of list private AdjListNode _cur; // current position of iterator public AdjList() { setHead(null); setCur(null); } private AdjListNode getHead() { return _head; } private void setHead(AdjListNode head) { _head = head; }

10

private AdjListNode getCur() { return _cur; } private void setCur(AdjListNode newCur) { _cur = newCur; } public void insertAtFront (int i) { AdjListNode newHead = new AdjListNode(i); newHead.setNext(getHead()); setHead(newHead); } // The next two methods illustrate an *iterator*. The purpose // is to allow the user to get successive elements of a list // without allowing him to muck around with the list nodes themselves, // which would prevent us from ensuring that he could // corrupt our list. public void resetIterator() // set iterator to beginning of list { setCur(_head); } public int getNextItem() // get next item in list and advance iterator { if (getCur() == null) return -1; else { int result = getCur().getItem(); setCur(getCur().getNext()); return result; } } // see handout public String toString() { String Result = ""; for (AdjListNode cur = getHead(); cur != null; Result += cur.getItem() + " "; return Result; }

cur = cur.getNext())

11

} Add the following method to the Graph class: int countVerticesFrom{int vertex, boolean [] Marked) Let us say that vertex i has been marked if Marked[i] == true. Marked is an array with getNumVertices() - 1 elements, The variable vertex is a vertex from 0 to getNumVertices() - 1. Another precondition is that vertex is not marked when the call is made on it. The postconditions are that all vertices reachable from vertex through vertices that were unmarked at the time of the call have been marked, and that a count of these vertices has been returned as the returned value. Hints: Notice resetIterator and getNextItem in the AdjList class. My solution has ten lines of code, not counting curly braces. public int countVerticesFrom(int vertex, boolean [] Marked) { Marked[vertex] = true; // mark the vertex youre called on int count = 1; // count that vertex AdjList L = getList(vertex); // get its adjacency list L.resetIterator(); // get the first neighbor of the vertex int neighbor = L.getNextItem(); while (neighbor != -1) // while you havent run out of neighbors { if (!Marked[neighbor]) // if the neighbor isnt marked ... // mark and count vertices reachable from it, add to total ... count += countVerticesFrom(neighbor, Marked); neighbor = L.getNextItem(); // get the next neighbor } return count; } }

12

9. The monk on the mountain who was supposed to solve the Towers of Hanoi problem died of old age before he nished. Not only that, but he went senile toward the end, and left the disks in quite a mess. Luckily, all the disks are still on the pegs and no disk is on top of a smaller disk. Write the following method to tell how to nish the job: void HanoiClutter(int [] Disk, int n, int dest) The precondition is that n is the number of disks, dest is where they are all supposed to end up, and Disk[0..n-1] tells the current peg number for each disk from 0 to n 1, where 0 is the smallest and n 1 is the largest. The pegs are numbered 1, 2, 3.

The postcondition is that a set of moves that gets all the disks to dest has been printed out. The move should tell which disk is being moved, which peg it is moving from, and which peg it is moving to.

Hints: If i and j are two peg numbers, then 6 i j is the number of the third peg. One possibility to consider is that disk n 1 is already at dest. My solution has nine lines of code, not counting curly braces. public static void HanoiClutter(int [] Disk, int n, int dest) { if (n == 0) return; else if (Disk[n-1] == dest) // if largest disk already at dest HanoiClutter(Disk, n-1, dest); // move the n-1 smaller disks there else { // get the n-1 smaller disks to the spare peg ... HanoiClutter (Disk, n-1, 6 - dest - Disk[n-1]); System.out.println ("Move disk " + (n-1) + " from " + Disk[n-1] + " to " + dest); // move largest to dest // record where its moved to so you can meet the precondition of last // recursive call Disk[n-1] = dest; HanoiClutter(Disk,n-1, dest); // get smaller n-1 disks to dest } }

13

Potrebbero piacerti anche