礼物的最大价值

礼物的最大价值

题目:在一个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(i1,j),f(i,j1))+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]

不论是代码量还是写起来的顺畅程度,都比回溯法简单太多了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值