背景 今天我接受了采访,并被问到以下问题。
您将获得一个网格。
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个可能的选择,并且我想获得两个选择中的最大值,但是由于某种原因,我无法获得正确的输出。
我有两个帮助函数,它们检查我的位置在网格内是否有效,是否击中了右上角以及是否击中了我的基本情况。
我很乐意输入信息,看看我哪里出错了。
答案 0 :(得分:1)
编辑:回答代码的修改版本
有关您的新解决方案的问题:
int currentSum = grid[row][col];
和sum = sum + grid[row][col];
用左下角的值初始化总和,并在getMax()
的初始调用中再次添加相同的值。这不是应该的样子。只需从0开始求和,就可以由getMax()
完成。
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);
}
}
请注意,如果所有条目均为非负数,则此版本适用于任何网格(只要堆栈足够大且我们不会处理太大的数字,例如,我们将不会出现整数溢出)。无论如何,具有否定条目的网格都可以通过这种方式进行操作,从而可以通过该算法找到最佳路径,并且可以轻松地将解决方案“转换”回原始网格(只需从每个条目中减去最小值)。
回答原始代码
我发现您的代码存在多个问题:
isValid(grid,row+1,col)
和sum1 = grid[row+1][col];
您正在尝试将添加 1到该行,但是您(正确)从int row = grid.length-1;
开始。加1将给您一个无效的位置,因此永远不会执行第一个分支。取而代之的是,您需要从行中减去 1以“上升”。
sum = sum + Math.max(sum1,sum2);
这会更改sum
,但看不到您朝哪个方向移动。然后直接...
getMax(grid,sum,row+1,col);
和getMax(grid,sum,row,col+1);
...用新的最大和进行递归调用,但同时从两个位置进行。为了获得正确的解决方案,您应该使用它们的方向表示的值来调用它们。还要注意,row+1
也需要在此处替换为row-1
。
return sum;
您现在返回此最大和,但完全忽略了递归调用的返回。相反,您应该比较它们的收益并返回两者的较高价值。
跟踪与动态编程
您的算法通常应该可以运行,并且对于较小的问题实例就足够了,但对于较大的实例来说就足够了(因为该算法将对每个步骤进行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