《剑指offer》python实现系列,全目录
题目描述:
在一个mxn的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0),你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格,直到到达棋盘的右下角。给定一个棋盘及其上面的礼物,请计算你最多能拿多少价值的礼物?
1 10 3 8
12 2 9 6
5 7 4 11
3 7 16 5
1,12,5,7,7,16,5可以拿到最大价值位53的礼物。
第一想法:
动态规划
剑指offer方法:
动态规划
$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]$
还需要一个辅助的二维数组缓存中甲的计算结果,数组中坐标为(i,j)的元素表示到达坐标为(i,j)的格子时能拿到的礼物价值总和的最大值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class Solution:
def get_max_value(self, values, rows, cols):
#矩阵左上角按00算
if not values or rows <= 0 or cols <= 0:
return 0
maxValues = [[0]*cols]*rows #辅助数组,保存中间结果
for i in range(rows):
for j in range(cols):
left = 0 #来自左边的礼物最大值
up = 0 #来自上边的礼物最大值
if i > 0:#i>0说明可以来自上边
up = maxValues[i-1][j]
if j > 0:#说明可以来自左边
left = maxValues[i][j-1]
#状态转移方程
maxValues[i][j] = max(up,left)+values[i*cols+j]
return maxValues[rows-1][cols-1] #返回右下角元素
print(Solution().get_max_value([1, 10, 3, 8, 12, 2, 9, 6, 5, 7, 4, 11, 3, 7, 16, 5], 4, 4))
优化
将二位辅助矩阵 改为 一维数组
拿到礼物的最大价值只依赖于(i-1,j)和 (i,j-1)的两个格子,因此可i-2行及更上面的所有格子以及j-2列及更左边的所有各自礼物的最大价值实际上没有必要保存下来。
可以用一个一维数组来替代前面代码中的二维矩阵 max Values。
该一维数组的长度为棋盘的列数n。当我们计算到达坐标为(i,j)的格子时能够拿到
该数组前面j个数字分别是当前第i行前面j个格子礼物的最大价值,而之后的数字分别保存前面第i-1行n-j个格子礼物的最大价值。就是此数组以j位分界线分成两部分,前半部分是当前行前j个元素的最大价值,后半部分是上一行后面(n-j)个元素的最大价值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class Solution:
def get_max_value(self, values, rows, cols):
#矩阵左上角按00算
if not values or rows <= 0 or cols <= 0:
return 0
maxValues = [0]*cols
for i in range(rows):
for j in range(cols):
left = 0 #来自左边的礼物最大值
up = 0 #来自上边的礼物最大值
if i > 0:#i>0说明可以来自上边
up = maxValues[j]
if j > 0:#说明可以来自左边
left = maxValues[j-1]
#状态转移方程
maxValues[j] = max(up,left)+values[i*cols+j]
return maxValues[-1] #返回右下角元素
print(Solution().get_max_value([1, 10, 3, 8, 12, 2, 9, 6, 5, 7, 4, 11, 3, 7, 16, 5], 4, 4))