Posted 24 October 2011 (corrected later that day), due at 11:59 p.m. EDT on Wednesday 2 November 2011, by placing .java files and .class files in your cs187 directory on your edlab account. Please place files for this assignment in a subdirectory of cs187 called "project5".
As we get questions on this assignment we will put answers on the Q&A page.
Some useful code is available in this directory on the course web site. We will make a sample driver available within a few days.
Goals of this project:
We have now implemented two different methods for finding a path from one cell to another in a maze. Project #2 used a stack for depth-first search, which tested whether there was a path and returned the path easily by reading the stack at the end. Project #4 used a queue for breadth-first search, finding the path with the least number of hops, and using parent pointers assigned during the search to return the path at the end. In this project, adapted from Problem J in the 2003 ACM Programming Contest, we are going to use an ordered list to find the best path according to a particular criterion.
Our path is to be traversed by a merchant who has a certain number of silver spoons at the source and needs to get as many of them as possible to the destination. The merchant must pay a toll on entering each (open) cell on the route. Cells are either "villages" that charge one spoon in toll, or "towns" that charge 5% of the spoons brought in, rounded up. (The three-parameter Maze constructor will need to be changed so we can say which open cells are towns and which villages -- we will do that for you.) The best route may thus depend on how many spoons he is transporting.
As in Project #4, we will keep a "queue" of cells that have been visited by our search, but this time it will be kept as an ordered list, ordered by the number of spoons we can bring to that cell. When we choose a new cell to investigate, we choose the one with the largest number of spoons we can bring. Investigating a cell means taking all of its open unseen neighbors, seeing how many spoons we can bring to each, and putting those cells into the ordered list, keeping the list ordered by number of spoons. We are only interested in paths that deliver a positive number of spoons.
We will want to treat the seen
field of our cells
differently in this search. In Project #4, once we found a path from
the source to a cell, we knew that no future path could be shorter,
and we could mark the cell as seen. But now we might discover a
better path later, so we should keep cells still in the queue
as "unseen". When an open unseen neighbor of the cell being
investigated turns out to be on the list, we must decide whether the
new path we have discovered is better or worse than the one we found
before, and adjust the queue accordingly.
As in Project #4, we will need parent pointers to be able to reconstruct the best path when we are done. Note that the best path may not be the one with the fewest number of hops -- the best path is the one that brings the most spoons to the cell. We can use the distance field of our cells to find the length of the best path from the source -- this will save us time in setting up the array to output as the best path.
We are giving you a class PQCell
that extends
QCell
and implements
Comparable<PQCell>
. It has two additional fields,
the first being
a boolean named isTown
that is true for a town and false
for a village. The second is an int named spoons
that
you will set during the search, as the number of spoons the merchant
could have there after paying the toll to enter. (A subtlety --
because the parent
field is inherited from
QCell
, its type is QCell
rather than
PQCell
. So you will need a cast operation to take a cell
out of a parent field and put it into the output array of the new
bestPath
method, which is an array of PQCell
objects.)
The Java library does not have a specific interface corresponding
to L&C's OrderedListADT
, so we will implement our ordered
list with a PriorityQueue<PQCell>
object. The
methods we will primarily need are add
, which we can think
of as putting a new PQCell
into its proper place in the
ordered list, and poll
, which will return and remove the
"smallest" element.
The classes for this project will be Maze
and
PQCell
. (This means that Cell
,
SCell
, and QCell
must also be present in the
directory for PQCell
to work.) The first new method of
Maze
should be public int spoons (int startSpoons,
int sx, int sy, int dx, int dy)
, which returns the maximum number
of spoons that can be delivered to (dx, dy) if you start
with startSpoons at (sx, sy). The second new method is public
PQCell [ ] bestPath
, with the same five int
parameters, which returns the path that delivers the maximum (positive)
number of spoons. If there is no way to deliver a positive number,
then spoons
should return 0 and bestPath
should return a 0-length array.
Note that the merchant does not pay the toll for the source node, but does pay to enter the destination, unless it is equal to the source.
Also note that neither of your two new methods should have any side
effect on the calling Maze
object, so you must clean up
any fields of any cells that are altered during the search.
You may borrow code from any of L&C's classes, or from our solutions, with specific attribution in a comment.
Last modified 24 October 2011