简体   繁体   中英

How can I make this method end its recursion and reach a maximum?

I ran into a StackOverflowError when running a solution I wrote to an assignment.

These are the exact instructions from the book Java Methods: A & AB :

Write a program in which Cookie Monster finds the optimal path from the upper left corner (0,0) to the lower right corner(SIZE-1,SIZE-1) in a cookie grid (a 2-D array). The elements of the grid contain cookies (a non-negative number) or barrels (-1). On each step, Cookie Monster can only go down or to the right. He is not allowed to step on barrels. The optimal path contains the largest number of cookies.

The program reads the cookie grid from a file and reports the number of cookies on the optimal path. (The path itself is not reported.) A sample data file is provided in JM\\Ch19\\Exercises\\cookies.dat.

Hint: Use a stack. If there is only one way to proceed from the current position, then go there and update the total accumulated number of cookies. If there are two ways to proceed, save one of the possible two points (and its total) on the stack and proceed to the other point. If you have reached the lower-right corner, update the maximum. If there is nowhere to go, examine the stack: pop a saved point, if any, and resume from there.

The goal is to give my teacher the best possible path (the one with the most "cookies" on it).

Okay. so the mentioned cookie map file is this:

 1  3  0  5 -1  7 -1 -1  0  4  2  1
-1  3  2  1 -1  4 -1  5  3 -1  1  0
 5  4  8 -1  3  2  2 -1  4 -1  0  0
 2  1  0  4  1 -1  8  0  2 -1  2  5
 1  4  0  1 -1  0  3  2  2  4  1  4
 0  1  4  1  1  6  1  4  5  2  1  0
 3  2  5  2  0  7 -1  2  1  0 -1  3
 0 -1  4 -1 -1  3  5  1  4  2  1  2
 5  4  8 -1  3  2  2 -1  4 -1  0  0
 2  1  0  4  1 -1  8  0  2 -1  2  5
 1  3  0  5 -1  7 -1 -1  0  4  2  1
 0  0  3  1  5  2  1  5  4  1  3  3

And this is the class I use to get a 2-D array of the numbers (I know this part works.) Using A BlueJ debugger, the 2-D array seems to be what I want it to be.

import java.util.*;
import java.io.*;

public class MapReader
{
    public static int[][] grid;
    public static Scanner gridscanner = null;
    public static int[][] getMap()
    {
        File file = new File("cookies.dat");
        try
        {
            gridscanner = new Scanner(file);
        }
        catch (FileNotFoundException ex)
        {
            System.out.println("*** Cannot open cookis.dat ***");
            System.exit(1);
        }

        int row = 12;
        grid = new int[row][row];

        for(int r = 0; r < row; r++)
        {
            for(int c = 0; c < row; c++)
            {
                grid[r][c] = gridscanner.nextInt();
            }
        }
        return grid;
    }
}

And here is a class I use to keep track of saved positions, their values, and their locations for when I'm traversing this "cookie map":

import java.util.*;

public class Point
{
    int row;
    int col;
    int total;

    public Point(int r, int c, int t)
    {
        row = r;
        col = c;
        total = t;
    }

    public int getRow()   { return row; }
    public int getCol()   { return col; }
    public int getValue() { return MapReader.getMap()[row][col]; }
    public int getTotal() { return total; }
}

And finally, here is the class that I use to recursively travel through the 2D array. You'll notice that I prefer to go right when two paths are available but then go down when I pop a Point from the "saved" Stack. The problem lies in this class as far as I know: How can I make the method end its recursion and reach a maximum?

import java.util.*;

public class CookieMonster
{
    private static int[][] map = MapReader.getMap();
    private static int max = 11;
    private static int total, maximum;
    private static Stack<Point> saved = new Stack<Point>();
    public static void main(String[] args)
    {        
        System.out.println(move(0,0));
    }

    public static int move(int r, int c)
    {
        int right = 0;
        int down = 0;
        boolean isright = true;
        boolean isdown = true;
        if (c < max)
        {
            right = map[r][c + 1];
        }
        else
            isright = false;
        if (r < max)
        {
            down = map[r + 1][c];
        }
        else
            isdown = false;
        if (right == -1)
            isright = false;
        if (down == -1)
            isdown = false;

        if (isright && isdown)
        {
            saved.push(new Point(r + 1, c, total + down));
            total += right;
            move(r, c + 1);
        }

        else if (isright)
        {
            total += right;
            move(r, c + 1);
        }

        else if (isdown)
        {
            total += down;
            move(r + 1, c);
        }

        else
        {
            if (r  == max && c == max)
            {
                if (maximum < total)
                    maximum = total;
            }
            if (!saved.isEmpty())
            {
                Point sd = saved.pop();
                total = sd.getTotal();
                move(sd.getRow(), sd.getCol());
            }
        }
        return maximum;
    }
}

I know the hint suggests to use a stack, but this problem can be solved much more efficiently using dynamic programming . It is basically recursion with a memory of previously visited paths to avoid recomputation.

Assuming you index the matrix starting at 1, Your cost function should be as follows:

c(i, j) = -INF if i == 0 or j == 0 or data(i, j) < 0
          data(1, 1) if i == 1 and j == 1
          data(i, j) + min(c(i, j - 1), c(i - 1, j)) 

You can iterate in the usual nested ij loop from left to right and up to down.

c(n, n) will give you the result of the optimal path.

This seems np-complete. Using a stack and writing the logic to return to a former path if you run into a dead-end seems like wasted effort. You are going to have to use a lot of brute force even with the stack and logic. It seems easier to just use a primitive to store the number of cookies and just write some logic to move down and right at random. If you hit a dead end, just throw that result out and start over. If you hit the end, save that value and check to see if it is larger than a previous path. It if it is, keep it until you find a larger one. If you run it enough times, you will find the best path. I can't imagine it would take more than a few seconds to find the most cookies.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM