在一个 m*n 的棋盘的每一个格都放有一个礼物,每个礼物都有一定价值(价值大于0)。从左上角开始拿礼物,每次向右或向下移动一格,直到到达棋盘的右下角。给定一个棋盘及上面的礼物,请计算你最多能拿到多少价值的礼物。
例如,在下面的棋盘中,如果沿着(1、12、5、7、7、16、5)的路线,那么我们能拿到最大价值为53的礼物。
1 10 3 8
12 2 9 6
5 7 4 11
3 7 16 5
- 功能测试(多行多列的矩阵;一行或者一列的矩阵;只有一个数字的矩阵)
- 特殊输入测试(指向矩阵数组的指针为空指针)
- 考察应聘者用动态规划分析问题的能力。
- 考察应聘者对递归及时间效率的理解。需要我们利用动态规划来减少重复计算。
首先我们要找出递归式:
f(i,j) = max(f(i-1,j),f(i,j-1)) + gift[i,j]
所以我们可能会马上用递归做出来(见自己解题),但是这种方法会有大量的重复计算,导致递归的代码不是最优的,所以我们考虑用动态规划(循环)来做。
我们需要一个二维数组,数组中坐标为(i,j)的元素表示到达坐标(i,j)的格子时能拿到的礼物价值总和的最大值。
代码见参考解题。
/**
* 礼物的最大价值
*
* @Author rex
* 2018/8/30
*/
public class Solution {
/**
* 递归解题
* @param board
* @return
*/
public int getMost(int[][] board) {
int i = board.length;
int j = board[0].length;
return getMost(board, i-1, j-1);
}
/**
* 计算当前坐标下的礼物最大值
* @param board
* @param i
* @param j
* @return
*/
public int getMost(int[][] board, int i, int j) {
if (i == 0 && j == 0) {
return board[i][j];
}
int left = 0;
int top = 0;
if (i >= 1) {
top = getMost(board, i - 1, j);
}
if (j >= 1) {
left = getMost(board, i, j - 1 );
}
return Math.max(top,left) + board[i][j];
}
不足:
没有做非空输入判断;有很多的重复计算
在使用递归的时候应想一想是否可以用动态规划来减少重复计算。
/**
* 礼物的最大价值
*
* @Author rex
* 2018/8/30
*/
public class Solution1 {
/**
* 动态规划解题
*
* @param board
* @return
*/
public int getMost(int[][] board) {
if (board == null || board.length == 0 || board[0].length == 0) {
return 0;
}
int rows = board.length;
int columns = board[0].length;
// 辅助数组,如果可以破坏原数组,则可直接对原数组重新赋值
int[][] temp = new int[rows][columns];
for (int i = 0; i< rows; i++) {
for (int j = 0; j < columns; j++) {
int value = board[i][j];
if (i == 0 && j == 0) {
// 存入当前值
temp[i][j] = value;
} else if (i == 0){
// 往右走
temp[i][j] = temp[i][j-1] + value;
} else if (j == 0) {
// 往下走
temp[i][j] = temp[i-1][j] + value;
} else {
// 挑一个最大的路径走
temp[i][j] = Math.max(temp[i][j-1], temp[i-1][j]) + value;
}
}
}
return temp[rows - 1][columns-1];
}
}
可以尝试在辅助数组上优化,减小空间复杂度。