题目描述
在m*n的棋盘里每格一个礼物,每个礼物有价值(大于0)。你从棋盘左上角开始拿礼物,并每次往左和下移动一格,直到达到棋盘右下角。问最多拿多少价值的礼物?
- 解题思路:
找某个位置的最大价值礼物,只要知道它的上面位置和左边位置的最大价值,再加上本身的价值,就能得出结果。递归可解。 - 几个注意点:
1.递归替换成循环,和之前一样,减少重复计算。 - 解题步骤:
先推递归式: f ( i , j ) = m a x ( f ( i , j − 1 ) , f ( i − 1 , j ) ) + g ( i , j ) f(i,j)=max(f(i,j-1),f(i-1,j))+g(i,j) f(i,j)=max(f(i,j−1),f(i−1,j))+g(i,j)
觉得不好看的话,就写个伪码吧 f ( i , j ) = m a x ( l e f t , u p ) + g ( i , j ) f(i,j)=max(left, up)+g(i,j) f(i,j)=max(left,up)+g(i,j)
其中g(i,j)是位置本身礼物的价值,f(i,j)表示当前拿到的最大礼物的价值,别弄混了。
然后遍历数组,算出每个位置的最大礼物价值,得到的价值用一个二维数组maxValues记录起来。
取出二维数组右下角的值即为答案。
【考点】 递归 动态规划
class Solution {
public:
int getMaxValue(const int* values, int rows, int cols) {
if (values == nullptr || rows <= 0 || cols <= 0)
return 0;
int** maxValues = new int*[rows];
for (int i = 0; i < rows; ++i)
maxValues[i] = new int[cols];
for (int i = 0; i < rows; ++i)
{
for (int j = 0; j < cols; ++j)
{
int left = 0;
int up = 0;
// 第一行和第一列特殊处理
if (i > 0)
up = maxValues[i - 1][j];
if (j > 0)
left = maxValues[i][j - 1];
// 递归式
maxValues[i][j] = max(left, up) + values[i*cols + j];
}
}
int maxValue = maxValues[rows - 1][cols - 1];
for (int i = 0; i < rows; ++i)
delete[] maxValues[i];
delete[] maxValues;
return maxValue;
}
};
测试代码
int main(){
Solution obj;
int* tmp= new int[16]{ 1,10,3,8,
12,2,9,6,
5,7,4,11,
3,7,16,5 };
int res = obj.getMaxValue(tmp, 4, 4);
cout << res << endl;
return 0;
}
Output
53
查缺补漏:
1.书上的二维数组的创建方法和上一篇面试题46的创建方法一样,使用堆上的空间而不是栈上,有点意思,单独揪出来记录下。记得同时写好delete,防止内存泄漏。
int** maxValues = new int*[rows];
for (int i = 0; i < rows; ++i)
maxValues[i] = new int[cols];
// 写new的同时写好delete是个好习惯
for (int i = 0; i < rows; ++i)
delete[] maxValues[i];
delete[] maxValues;
2.原书对上述代码进行了优化,主要是优化空间:二维数组空间优化成一维数组空间。因为对于(i,j)来说,如图,左上部分已经没必要记录了,所以只需要记录
f
(
i
,
0
)
f(i,0)
f(i,0)到
f
(
i
,
j
−
1
)
f(i,j-1)
f(i,j−1),
f
(
i
−
1
,
j
)
f(i-1,j)
f(i−1,j)到
f
(
i
−
1
,
r
o
w
s
−
1
)
f(i-1,rows-1)
f(i−1,rows−1)即可。
这个一维数组的第j-1位是
f
(
i
,
j
−
1
)
f(i,j-1)
f(i,j−1),第j位是
f
(
i
−
1
,
j
)
f(i-1,j)
f(i−1,j),分别是左边和上边的最大礼物值。
之前二维数组的Output是
1 11 14 22
13 15 24 30
18 25 29 41
21 32 48 53
对比跑上面的测试例子,得出的一维数组Output是
21 32 48 53
当输出右下角的最大礼物的时候,刚好只存了最下面一行的最大礼物。
class Solution {
public:
int getMaxValue(const int* values, int rows, int cols) {
if (values == nullptr || rows <= 0 || cols <= 0)
return 0;
int* maxValues = new int[rows];
for (int i = 0; i < rows; ++i)
{
for (int j = 0; j < cols; ++j)
{
int left = 0;
int up = 0;
// 第一行和第一列特殊处理
if (i > 0)
up = maxValues[j];
if (j > 0)
left = maxValues[j - 1];
// 递归式
maxValues[j] = max(left, up) + values[i*cols + j];
}
}
int maxValue = maxValues[cols - 1];
delete[] maxValues;
return maxValue;
}
};