在网格中找到最佳路径

时间:2019-09-10 00:51:55

标签: java algorithm recursion dynamic-programming backtracking

背景 今天我接受了采访,并被问到以下问题。

您将获得一个网格。

  int [][] grid =
                {
                {0,0,0,2,5},
                {0,0,0,1,5},
                {0,1,1,1,1},
                {2,0,0,0,0}
                };

您从网格左下角开始。您只能。想法是到达 TOP右上角。您应该走的道路将带给您最大的价值。

  

上面的输出将是16

我的解决方案

public static int getPathMaxSum(int [][] grid){
    int row = grid.length-1;
    int col = 0;
    int currentSum = grid[row][col];
    return getMax(grid,currentSum,row,col);
}

public static int getMax(int [][] grid,int sum,int row,int col){
    if((isTopRight(grid,row,col)) || (!isValid(grid,row,col))){
        return sum;
    }else{
        sum = sum + grid[row][col];
        return Math.max(getMax(grid,sum,row-1,col),getMax(grid,sum,row,col+1));
    }
}

public static boolean isTopRight(int [][] grid, int row, int col){
    return row == 0 && col == grid[row].length-1;
}

public static boolean isValid(int [][] grid, int row, int col){
    return (row >= 0 &&  row < grid.length) && (col >= 0 && col < grid[row].length);
}

我试图递归地解决这个问题。我认为我在每个位置都有2个可能的选择,并且我想获得两个选择中的最大值,但是由于某种原因,我无法获得正确的输出。

我有两个帮助函数,它们检查我的位置在网格内是否有效,是否击中了右上角以及是否击中了我的基本情况。

我很乐意输入信息,看看我哪里出错了。

2 个答案:

答案 0 :(得分:1)

编辑:回答代码的修改版本

有关您的新解决方案的问题:

  1. int currentSum = grid[row][col];sum = sum + grid[row][col];

    用左下角的值初始化总和,并在getMax()的初始调用中再次添加相同的值。这不是应该的样子。只需从0开始求和,就可以由getMax()完成。

  2. if((isTopRight(grid,row,col)) || (!isValid(grid,row,col)))然后return sum;

    对于无效位置,这将起作用(请参见我的代码下方的限制),但不适用于右上角(因为我们尚未添加角的值)。因此,将两个条件分开,仅直接返回无效位置。在任何其他位置上,首先添加值,然后,如果达到“目标”,则返回总和。否则,返回“ going right”和“ going up”的最大值(递归调用现在是正确的)。

为解决这些问题并实现您的示例,我导出了以下代码:

public class Pathfinder {

    public static void main(String... args) {
        int [][] grid = {
            {0,0,0,2,5},
            {0,0,0,1,5},
            {0,1,1,1,1},
            {2,0,0,0,0}
        };

        System.out.println(getPathMaxSum(grid));
    }

    public static int getPathMaxSum(int[][] grid) {
        int row = grid.length - 1;
        int col = 0;

        return getMax(grid, 0, row, col);
    }

    public static int getMax(int[][] grid, int sum, int row, int col) {
        if(!isValid(grid, row, col))
            return sum;

        sum = sum + grid[row][col];

        if(isTopRight(grid, row, col))
            return sum;

        return Math.max(getMax(grid, sum, row - 1, col), getMax(grid, sum, row, col + 1));
    }

    public static boolean isTopRight(int[][] grid, int row, int col) {
        return row == 0 && col == grid[row].length - 1;
    }

    public static boolean isValid(int[][] grid, int row, int col) {
        return (row >= 0 &&  row < grid.length) && (col >= 0 && col < grid[row].length);
    }
}

请注意,如果所有条目均为非负数,则此版本适用于任何网格(只要堆栈足够大且我们不会处理太大的数字,例如,我们将不会出现整数溢出)。无论如何,具有否定条目的网格都可以通过这种方式进行操作,从而可以通过该算法找到最佳路径,并且可以轻松地将解决方案“转换”回原始网格(只需从每个条目中减去最小值)。

回答原始代码

我发现您的代码存在多个问题:

  1. isValid(grid,row+1,col)sum1 = grid[row+1][col];

    您正在尝试将添加 1到该行,但是您(正确)从int row = grid.length-1;开始。加1将给您一个无效的位置,因此永远不会执行第一个分支。取而代之的是,您需要从行中减去 1以“上升”。

  2. sum = sum + Math.max(sum1,sum2);

    这会更改sum,但看不到您朝哪个方向移动。然后直接...

  3. getMax(grid,sum,row+1,col);getMax(grid,sum,row,col+1);

    ...用新的最大和进行递归调用,但同时从两个位置进行。为了获得正确的解决方案,您应该使用它们的方向表示的值来调用它们。还要注意,row+1也需要在此处替换为row-1

  4. return sum;

    您现在返回此最大和,但完全忽略了递归调用的返回。相反,您应该比较它们的收益并返回两者的较高价值。

  5. 跟踪与动态编程

    您的算法通常应该可以运行,并且对于较小的问题实例就足够了,但对于较大的实例来说就足够了(因为该算法将对每个步骤进行2次递归调用,并且您有2 *(n-1)个步骤..导致指数运行时)。二次运行时的另一种方法是通过仅向右或向上看一个字段,然后将当前字段的值加到该字段的最大值,来向后浏览该字段并选择最佳方式。只需从右上角开始,然后从右到左逐行向左走。

答案 1 :(得分:1)

您的方法中不需要sum参数。

我假设您已经了解如何使用递归自顶向下方法解决此问题。 但是为了完整起见,基本公式还是:

您从row处的一个单元格开始,col获得其值,然后查找UP (row-1, col)或RIGHT (row, col+1)

因此结果将是:

grid[row][col] + Math.max( getMax( row-1, col, grid ), getMax( row, col+1, grid ) )

基本条件:

a)如果它位于右上角,即目的地,则无需递归,只需返回该单元格处的值即可。

b)如果它是无效的单元格(如您在isValid方法中编写的那样,则需要返回Integer.MIN_VALUE,因为其他单元格中的值可能为负,而您希望它们为最大。

因此您的getMax函数必须为:

public static int getMax(int [][] grid,int row,int col){
    if (isTopRight(grid,row,col)) {
       return grid[row][col];
    } else if (!isValid(grid,row,col)){
        return Integer.MIN_VALUE;
    } else {
        return grid[row][col] + Math.max(getMax(grid,row-1,col),getMax(grid,row,col+1));
    }
}

您可以看到有效的示例here