【剑指offer】面试题47:礼物的最大价值

13 篇文章 0 订阅
13 篇文章 0 订阅

题目描述
在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,j1),f(i1,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,j1) f ( i − 1 , j ) f(i-1,j) f(i1,j) f ( i − 1 , r o w s − 1 ) f(i-1,rows-1) f(i1,rows1)即可。
在这里插入图片描述
这个一维数组的第j-1位是 f ( i , j − 1 ) f(i,j-1) f(i,j1),第j位是 f ( i − 1 , j ) f(i-1,j) f(i1,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;
	}
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值