Question text is in black, solutions in blue. The scale was A = 90, C = 54.
Q1: 10 points Q2: 15 points Q3: 20 points Q4: 15 points Q5: 15 points Q6: 25 points Total: 100 points
We let P(n) be the statement "any simple path of n edges has exactly n+1
nodes". For the base case, P(0) says "any simple path of 0 edges has exactly
one node". This is true because the only simple path of 0 edges from a node
goes to that node itself, and so that node is the only one visited.
For the inductive case, we assume P(n) and prove P(n+1), which says that any
simple path of n+1 edges visits exactly n+2 nodes. Let α be an arbitrary
simple path of n+1 edges, from some node s to some node t. By the definition of
paths, α consists of some path β with n edges from s to some node x,
followed by the edge (x,t). By the IH, β visits exactly n+1 nodes. Thus
α visits those n+1 nodes plus the node t, and since α is a simple
path, x cannot be equal to any of the n+1 nodes visited by β. So α
visits exactly n+2 nodes, and since α was arbitrary we have proved P(n+1)
from the assumption of P(n). Since we have completed the induction, we have
proved P(n) for all naturals n.
node firstChild()
,
node nextChild()
, and boolean isAnotherChild()
in the
Node
class.
In English, we return 1 in the case of a single node, since it is a leaf. If
the root
node has children, we recursively compute the number of leaves under each
child and add these numbers together (since a leaf under the root must be under
exactly one of the root's children).
In pseudo-Java:
natural numLeaves (node v)
{// returns number of leaves in subtree under v
if (v.isLeaf()) return 1;
node w = v.firstChild();
natural temp = numLeaves(w);
while (v.isAnotherChild())
temp += numLeaves(v.nextChild());
return temp;}
Questions 3 and 4 deal with a particular kind of rooted directed trees called foo-trees. They are defined recursively:
Here is an example of a foo-tree with eight nodes:
(a) / | \ / | \ / | \ / | \ / | \ / | \ (b) (c) (d) / | \ / | \ / | \ / | \ / | \ / | \ (e) (f) (g) | | | | | | (h)
Base case: T is a single node v. The BFS and DFS of T both visit just v and
so visit the same nodes in the same order.
Now assume that T is constructed by clause (ii) of the definition, so that
every child of T except for the last one is a leaf, and the last child is the
root of a foo-tree U. Assume as the IH that the BFS and DFS of U visit the
nodes of U in the same order. Consider the BFS and DFS of T. The BFS first
visits the children of T in order and puts them on the queue. It then takes the
children before the last child off the queue and does nothing further, because
they are leaves. At this point the only node on the queue is the root of U, and
the BFS then proceeds identically to the BFS of U. The DFS of T
also visits the children of T before the last child in order, because it visits
each one, finds that it has no children, and proceeds to the next. When it
reaches the root of U, it proceeds identically to the DFS of U. Thus both BFS
and DFS visit the non-last children in order, then visit the nodes of U. They
each visit the nodes of U in the same order by the IH, so overall they visit
the nodes of T in the same order. By clause (iii) of the definition of
foo-trees, we have proved our property for all foo-trees.
We prove the contrapositive of the given statement -- that if a rooted directed
tree T is not a foo-tree, then the BFS and DFS of T do not visit the
nodes of T in the same order. We do this by induction on all non-foo trees T.
The base case is when T's root has a child v that is not last and not a leaf.
In this case the BFS visits v's next sibling right after v, while the DFS visits
v's first child after v, so the order of the two searches is different.
The recursive case is when the last child of T is a non-foo tree U. Here the
IH tells us that the BFS and DFS of U visit the nodes of U in different order,
and so the BFS and DFS of T, since they simulate this search, must also visit
nodes in different order.
Questions 5 and 6 deal with the following labeled undirected graph G. The node set is {a,b,c,d} and there are four edges: (a,c) with weight 4, (a,d) with weight 1, (b,c) with weight 1, and (b,d) with weight 1.
Many people did not remember the complete definition of the
single-step distance matrix. The entries on the diagonal are 0, because that is
the single-step distance from each node to itself. The entries for edges that
don't exist are &infty;, because there is no finite single-step distance between
the endpoints of a non-edge. This makes A equal to:
( 0 inf 4 1 )
(inf 0 1 1 )
( 4 1 0 inf)
( 1 1 inf 0 )
A^2(a,a) = min (0+0, inf+inf, 4+4, 1+1) = 0
A^2(a,b) = min (0+inf, inf+0, 4+1, 1+1) = 2
A^2(a,c) = min (0+4, inf+1, 4+0, 1+inf) = 4
A^2(a,d) = min (0+1, inf+1, 4+inf, 1+0) = 1
A^2(b,b) = A^2 (c,c) = A^2(d,d) = 0, similar to A^2(a,a)
A^2(b,c) = min (inf+4, 0+1, 1+0, 1+inf) = 1
A^2(b,d) = min (inf+1, 0+1, 1+inf, 1+0) = 1
A^2(c,d) = min (4+1, 1+1, 0+inf, inf+0) = 2
Since A^2 is symmetric, it is:
( 0 2 4 1 )
( 2 0 1 1 )
( 4 1 0 2 )
( 1 1 2 0 )
We find A^3 by min-plus multiplying A by A^2 -- since n-1 = 3, A^3 is the final
shortest-path distance matrix. The only entries that are different between
A^2 and A^3 are (a,c) and (c,a):
A^3(a,c) = min (0+4, inf+1, 4+0, 1+2) = 3
A^3 = ( 0 2 3 1 )
( 2 0 1 1 )
( 3 1 0 2 )
( 1 1 2 0 )
The queue starts with node a, with priority 0. We process (a,0), removing it
from the queue and adding (c,4) and (d,1). The next node off the queue is
(d,1), which we process to get (b,2). (We recognize a as already visited.)
Then (b,2) is next off the queue, and we process it to get (c,3). Finally
(c,3) comes off the queue before (c,4), and all nodes have left the queue.
In the search tree, each node appears at the end of the edge by which its
chosen entry entered the queue. So we have the edges from a to d, from d to b,
and from b to c. The edge from a to c is a non-tree edge because the entry for
that edge was still on the queue when the last node was visited. The tree
thus looks like:
The shortest path from a
to each node is the path using tree edges -- the empty
path of length 0 to a, the path of one edge and distance 1 to d, the path
of two edges and distance 2 from a through d to b, and finally the three-edge
path of distance 3 from a through d and b to c.
(a)
| \
| \
v |
(d) |
| |
| | non-tree edge from a to c
v |
(b) |
| |
| /
v v
(c)
The BFS from c begins with just c on the queue. It puts a and b on the queue,
then removes a and processes it to find d, the last node to be found. The BFS
tree looks like this, with a non-tree edge between b and d (assuming that
the neighbors of c are placed on the queue in order, with a before b):
Thus we have h(a) = 1, h(b) = 1, h(c) = 0, and h(d) = 2.
If g(v) is the true distance in G (with weighted edges) from v to c, then
h is an admissible heuristic for g because 0 ≤ h(v) ≤ g(v) for any node
v. (This is where it comes in that the weights in G are all at least 1 -- if
they were not we might have g(v) < h(v).) We could thus use h in an
A* search of G from a, using c as a goal node. The initial entry
for a would be (a,0+1), because k(a) = 0 and h(a) = 1. We take this entry off
the queue and add (c,4+0) and (d,1+2). We take (d,3) off the queue and add
(b,2+1). We take (b,3) off before (c,4), and add (c,3+0). When we take (c,3)
off the queue, it represents the three-step path of distance 3. In this case
the A* did not save us any time relative to the uniform-cost search.
(c)
/ \
/ \
v v
(a) (b)
| /
| /
| /
| /
v v
(d)
Last modified 26 November 2007