在一个 m*n 的棋盘中的每一个格都放一个礼物,每个礼物都有一定的价值(价值大于0).你可以从棋盘的左上角开始拿各种里的礼物,并每次向左或者向下移动一格,直到到达棋盘的右下角。给定一个棋盘及上面个的礼物,请计算你最多能拿走多少价值的礼物?
比如说现在有一个如下的棋盘,
1 10 3 8
12 2 9 6
5 7 4 11
3 7 16 5
在这个棋盘中,按照(1,12,5,7,7,16,5)的顺序可以拿到总价值最大的礼物。礼物的最大价值为1+12+5+7+7+16+5=53。
思路一:递归,定义一个函数f(i,j)用来表示到达座标为(i,j)的格子时能拿到的礼物总和的最大值,根据题意只能从右走和下走,所以对当前点(i,j)来说,最大值只需比较左边和上面的。另外考虑当处在上边界和左边界的情况时只有一个方向可以走。
public static int getMaxValue(int[][]a){
int max = 0;
int i = a.length;
int j = a[0].length;
max = sum(a,i,j);
return max;
}
private static int sum(int[][] a,int row,int col) {
//当来到左上角时,终止条件
if(row==1&&col==1)
return a[0][0];
//当来到上边界,只能往左走
if(row==1)
return sum(a, row, col-1)+a[row-1][col-1];
//当来到左边界,只能往上走
else if(col==1)
return sum(a, row-1, col) + a[row-1][col-1];
//否则判断是哪边大
else
return Math.max(sum(a, row, col-1), sum(a, row-1, col))+a[row-1][col-1];
}
思路二:采用动态规划,因为递归会存在大量的重复计算,效率很低,我们可以采用一个二维数组来保存当前点的最大值,这样就可以计算得右下角那点的最大值。
public static int getMaxValue(int[][] a) {
if(a==null||a.length==0||a[0].length==0)
return 0;
int row = a.length;
int col = a[0].length;
//定义二维数组来保存中间结果
int[][] maxValues = new int[row][col];
for(int i = 0;i < row;i++)
for(int j =0;j < col;j++)
{
//原点
if(i==0 && j==0)
{
maxValues[i][j] = a[i][j];
}
//上边界
else if(i==0 && j!=0)
{
maxValues[i][j] = a[i][j] + maxValues[i][j-1];
}
//左边界
else if(i!=0&&j==0)
{
maxValues[i][j] = a[i][j] + maxValues[i-1][j];
}
else {
maxValues[i][j] = a[i][j] + Math.max(maxValues[i][j-1], maxValues[i-1][j]);
}
}
return maxValues[row-1][col-1];
}
思路三:因为节点一直是往右下走的,所以i-2行的数据并不需要保存。