礼物的最大价值
题目:在一个m × \times ×n的期盼的每一个都放有一个礼物,每个礼物都有一定的价值。你可以从棋盘的左上角开始拿格子里的礼物,并每次向左或者向下移动一格,直到达到棋盘的右下角。给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物?
例如,在下面的棋盘中,可以拿到最大价值为53的礼物
1 10 3 8
12 2 9 6
5 7 4 11
5 7 16 5
对于这个问题,开始的思路就是回溯法,求出从左上角开始所有能到达右下角的路径,并计算这条路径上的长度,并维护一个max_path,最终返回的max_path值为最后的最大价值,代码如下:
def find_path(nums):
rows = len(nums)
cols = len(nums[0])
visited = [[0 for i in range(cols)] for j in range(rows)]
max_num = 0
count = 0
find_max_path(nums, 0, 0, rows, cols, visited, max_num, count)
return max_num[0]
def find_max_path(nums, row, col, rows, cols, visited, max_num, count):
# 终止条件,意思是到达边界重点
# 此时需要比较并更新路径最大值
if row == rows - 1 and col == cols - 1:
count += nums[row][col]
if max_num < count:
max_num = count
return
visited[row][col] = 1
count += nums[row][col]
if row < rows - 1 and visited[row + 1][col] != 1:
find_max_path(nums, row + 1, col, rows, cols, visited, max_num, count)
if col < cols - 1 and visited[row][col + 1] != 1:
find_max_path(nums, row, col + 1, rows, cols, visited, max_num, count)
visited[row][col] = 0
这是开始写出的代码,是个错误的代码,其错误原因就是并不能通过max_num = count的语句来使max_num的值得到更新,尽管在当前栈里的max_num已经更新,但是在赋值时其实是产生了一个新的max_num,如果跟踪max_num的id可以发现,max_num的id在赋值时发生变化,这个新的max_num并不会影响到上一个调用栈里max_num的值,因此在递归中这样改变一个整数的值不可行。
需要通过其他的办法来保存之前max_num的值,并且让这个对所有的函数栈都可见且不变,想到之前写遍历树的路径及全排列的题目,需要保存最终答案时都是使用的list,因此将max_num转化为list来更新。list本身就是可变的,更改其中的元素数值不会改变list的id,因此对于所有的栈全程可见。
def find_path(nums):
rows = len(nums)
cols = len(nums[0])
visited = [[0 for i in range(cols)] for j in range(rows)]
max_num = [0]
count = 0
find_max_path(nums, 0, 0, rows, cols, visited, max_num, count)
return max_num[0]
def find_max_path(nums, row, col, rows, cols, visited, max_num, count):
# 终止条件,意思是到达边界重点
# 此时需要比较并更新路径最大值
if row == rows - 1 and col == cols - 1:
count += nums[row][col]
if max_num[0] < count:
max_num[0] = count
return
visited[row][col] = 1
count += nums[row][col]
if row < rows - 1 and visited[row + 1][col] != 1:
find_max_path(nums, row + 1, col, rows, cols, visited, max_num, count)
if col < cols - 1 and visited[row][col + 1] != 1:
find_max_path(nums, row, col + 1, rows, cols, visited, max_num, count)
visited[row][col] = 0
这是我对这题的思路,但这个思路放在笔试里感觉大概率超时,递归多,确实有点慢,参考《剑指offer》中动态规划的思路,递推公式为:
f
(
i
,
j
)
=
m
a
x
(
f
(
i
−
1
,
j
)
,
f
(
i
,
j
−
1
)
)
+
g
i
f
t
[
i
,
j
]
f(i,j)=max(f(i-1,j),f(i,j-1)) + gift[i,j]
f(i,j)=max(f(i−1,j),f(i,j−1))+gift[i,j]
公式挺好理解的,这里子问题的坐标是从小到大,因此循环从前面开始就可以。
def find_path(nums):
rows = len(nums)
cols = len(nums[0])
max_values = [[0 for i in range(cols)] for j in range(rows)]
for i in range(rows):
for j in range(cols):
up, left = 0, 0
if i > 0:
up = max_values[i - 1][j]
left = max_values[i][j - 1]
max_values[i][j] = max(up, left) + nums[i][j]
return max_values[-1][-1]
不论是代码量还是写起来的顺畅程度,都比回溯法简单太多了。