剑指offer 学习笔记 礼物的最大价值
面试题47:礼物的最大价值。在一个mxn的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0)。你从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格,直到到达棋盘的右下角。给定一个棋盘和上面的礼物,请计算你最多能拿到多少价值的礼物。
这是一个典型的能用动态规划解决的问题,先用递归思路来分析,我们先定义第一个函数f(i,j)表示到达坐标为(i,j)的格子时能拿到的礼物总和的最大值,我们有两种可能的途径到达坐标为(i,j)的格子,即通过格子(i-1,j)和(i,j-1),所以f(i,j)=max(f(i-1,j),f(i,j-1))+gift[i,j]。我们用递归分析问题,但由于有大量的重复计算,基于循环从底向上计算效率要高很多,为了缓存中间的计算结果,我们需要一个辅助的二维数组,以下代码以下图中棋盘为例,下划线元素路径为最大价值路径,最大价值为53:
#include <iostream>
using namespace std;
int GetMaxValue(const int* values, int rows, int cols) {
if (values == nullptr || rows < 1 || cols < 1) {
return 0;
}
int** res = new int* [rows](); // 加上圆括号相当于值初始化(将res中所有指针值初始化为空指针)堆内存,否则其中内容是未定义的
for (int i = 0; i < rows; ++i) {
res[i] = new int[cols](); // 加上圆括号相当于值初始化(将res[i]中所有元素值初始化为0)堆内存,否则其中内容是未定义的
}
for (int row = 0; row < rows; ++row) {
for (int col = 0; col < cols; ++col) {
int up = 0;
if (row != 0) {
up = res[row - 1][col];
}
int left = 0;
if (col != 0) {
left = res[row][col - 1];
}
res[row][col] = max(up, left) + values[row * cols + col];
}
}
int maxValue = res[rows - 1][cols - 1];
for (int i = 0; i < rows; ++i) {
delete[] res[i];
}
delete[] res;
return maxValue;
}
int main() {
int values[] = { 1,10,3,8,12,2,9,6,5,7,4,11,3,7,16,5 };
cout << GetMaxValue(values, 4, 4) << endl;
}
我们还可以进一步优化代码,到达坐标为(i,j)的格子时能够拿到的最大礼物价值只依赖于(i-1,j)和(i,j-1)格子上的最大礼物价值,因此第i-2行及以上的计算结果没有必要缓存下来,我们可以使用一个一维数组代替上例代码中的二维数组,该一维数组的长度为棋盘列数cols,要计算坐标为(i,j)的格子的最大礼物价值f(i,j),数组中前j个数字分别为f(i,0)、f(i,1)、…、f(i,j-1),数组中从下标为j的数字开始到最后一个数字,分别为f(i-1,j)、f(i-1,j+1)、…、f(i-1,n-1)。即数组前j个数字保存当前第i行前面j个格子礼物的最大价值,而之后的数字分别保存上一行第i-1行从第j个格子开始的最大礼物价值。当计算到f(i,j)时,直接从一维数组的第j个元素的左边元素和第j个元素中选出较大一个,再和第(i,j)上的礼物价值相加即为(i,j)上的最大礼物价值:
#include <iostream>
using namespace std;
int GetMaxValue(const int* values, int rows, int cols) {
if (values == nullptr || rows < 1 || cols < 1) {
return 0;
}
int* res = new int[cols];
for (int row = 0; row < rows; ++row) {
for (int col = 0; col < cols; ++col) {
int up = 0;
if (row != 0) {
up = res[col]; // 坐标为(i,j)的格子的上面格子的最大礼物值即在一维数组的下标为j的格子上
}
int left = 0;
if (col != 0) {
left = res[col - 1]; // 坐标为(i,j)的格子的左面格子的最大礼物值即在一维数组的下标为j-1的格子上
}
res[col] = max(up, left) + values[row * cols + col];
}
}
int maxValue = res[cols - 1];
delete[] res;
return maxValue;
}
int main() {
int values[] = { 1,10,3,8,12,2,9,6,5,7,4,11,3,7,16,5 };
cout << GetMaxValue(values, 4, 4) << endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)