• 741摘樱桃


    题目: 一个N x N的网格(grid) 代表了一块樱桃地,每个格子由以下三种数字的一种来表示:
        0 表示这个格子是空的,所以你可以穿过它。
        1 表示这个格子里装着一个樱桃,你可以摘到樱桃然后穿过它。
        -1 表示这个格子里有荆棘,挡着你的路。
    你的任务是在遵守下列规则的情况下,尽可能的摘到最多樱桃:
        从位置 (0, 0) 出发,最后到达 (N-1, N-1) ,只能向下或向右走,并且只能穿越有效的格子(即只可以穿过值为0或者1的格子);
        当到达 (N-1, N-1) 后,你要继续走,直到返回到 (0, 0) ,只能向上或向左走,并且只能穿越有效的格子;
        当你经过一个格子且这个格子包含一个樱桃时,你将摘到樱桃并且这个格子会变成空的(值变为0);
        如果在 (0, 0) 和 (N-1, N-1) 之间不存在一条可经过的路径,则没有任何一个樱桃能被摘到。

    来源: https://leetcode-cn.com/problems/cherry-pickup/

    法一: 自己的错误代码

    思路: 要充分分析题意,原问题并不是分别求两次路径的最大值.下面代码先求最大值的路径,并记录取每个最大值路径的方向.再根据记录的方向把最大值的路径删除,再求第二个最大路径

    from typing import List
    class Solution:
        def cherryPickup(self, grid: List[List[int]]) -> int:
            # 求行数和列数
            r,c = len(grid),len(grid[0])
            # 第二个数先赋值为1
            grid[0][0] = (grid[0][0], grid[0][0], 0)
            # 处理第一列和第一行,如果存在一个-1的格子,则将其后面的都置为-1
            # 并记录路径,如果是从左边来的记为1,从上边来的记为-1
            # 每个tuple第一个元素记录和,第二个记录当前的原始值,第三个记录路径
            for i in range(1,r):
                if grid[i][0] != -1:
                    grid[i][0] = (grid[i][0]+grid[i-1][0][0], grid[i][0], -1)
                    continue
                else:
                    for j in range(i,r):
                        grid[i][0] = (-1,-1,-1)
                break
            for i in range(1,c):
                if grid[0][i] != -1:
                    grid[0][i] = (grid[0][i]+grid[0][i-1][0], grid[0][i],  1)
                    continue
                else:
                    for j in range(i,c):
                        grid[0][i] = (-1,-1,1)
                break
            # 先求第一次路径的最大值,要记录路径
            for p in range(1,r):
                for q in range(1,c):
                    if grid[p][q] == -1:
                        grid[p][q] = (-1,-1,1)
                    # 先判断上一行的格子里面是否有荆棘
                    elif grid[p-1][q][0] < 0:
                        # 如果前一列的也有荆棘,则把和赋值-1
                        if grid[p][q-1][0] < 0:
                            grid[p][q] = (-1,-1,1)
                        else:
                            grid[p][q] = (grid[p][q-1][0]+grid[p][q], grid[p][q], 1)
                    # 此时上一行的格子无荆棘
                    elif grid[p][q-1][0] < 0:
                        # 上边和左边的格子都有荆棘则和和置为-1
                        grid[p][q] = (-1,-1,1)
                    # 此时都没有荆棘
                    elif grid[p][q-1][0] >= grid[p-1][q][0]:
                        # 左边的大
                        grid[p][q] = (grid[p][q-1][0]+grid[p][q], grid[p][q],  1)
                    else:
                        # 上边的大
                        grid[p][q] = (grid[p-1][q][0]+grid[p][q], grid[p][q], -1)
            answer1 = grid[-1][-1][0]
            print('kk',answer1)
            print(grid)
            # 根据所做的标记把第一次遍历的最大值的路径删除,即把樱桃置0
            m, n = r-1, c-1
            while not grid[0][0][2]:
                sign = grid[m][n][2]
                # 如果大于0说明是从左边来的,则将列减1
                if sign > 0:
                    n -= 1
                    grid[m][n] = (0,0,grid[m][n][2])
                # 否则是从上边来的
                elif sign < 0:
                    m -= 1
                    grid[m][n] = (0,0,grid[m][n][2])
                # 否则等于0,说明是到左上角了
                else:
                    grid[0][0] = (0,0,1)
            # 将最后一个格子置为0
            grid[-1][-1] = (0,0,0)
            print(grid)
            # 再从左上角到右下角走一遍
            for p in range(1,r):
                for q in range(1,c):
                    if grid[p][q][1] == -1:
                        pass
                    # 如果前一列为荆棘,则上一行必不为荆棘,否则上一个if条件判断的时候已经pass掉了
                    elif grid[p][q-1][1] == -1:
                        grid[p][q] = (grid[p-1][q][0]+grid[p][q][1],grid[p][q][1],grid[p][q][2])
                    elif grid[p-1][q][1] == -1:
                        grid[p][q] = (grid[p][q-1][0]+grid[p][q][1],grid[p][q][1],grid[p][q][2])
                    else:
                        # 注意这里一定要在最外面加括号才能保证是tuple,否则的话是int,
                        grid[p][q] = (max(grid[p-1][q][0],grid[p][q-1][0]) + grid[p][q][1],grid[p][q][1],grid[p][q][2])
            return answer1 + grid[-1][-1][0]
    View Code

    法二: 官方动态规划方法

    思路: 典型的三维dp题,因为有三个变量,最巧妙的地方在于对边界值的处理,树的最终端的返回值只有两种情况,一个是-inf,另一个是到达(N-1,N-1)处.没完成四个分支的遍历后,记录并返回四个分支中的最大值.

    class Solution(object):
        def cherryPickup(self, grid):
            N = len(grid)
            # 用于记录回溯函数的输入和输出值.
            memo = [[[None] * N for _1 in range(N)] for _2 in range(N)]
            def dp(r1, c1, c2):
                r2 = r1 + c1 - c2
                # 如果遇到荆棘了,或者是从边上超出去了,则返回负无穷,相当于直接结束了这条路径
                # 这里实际上是把边上围了一圈荆棘
                if (N == r1 or N == r2 or N == c1 or N == c2 or
                        grid[r1][c1] == -1 or grid[r2][c2] == -1):
                    return float('-inf')
                # 如果一个点走到最后一个格子了,另一个也必定到最后一个格子了,返回值.
                # 如果没有这个条件,回溯的时候路径会走到(N,N-1)或(N-1,N)而返回'-inf',最终的dp也会返回-inf,
                # 因为-inf加上任意一个数都是-inf,有这个条件任意一条路径的终点都在(N-1,N-1)这个点上.
                elif r1 == c1 == N-1:
                    # 这里会输出四个值,是因为甲乙进入最后一个格子有四种方法
                    print(grid[r1][c1])
                    return grid[r1][c1]
                # 如果之前遍历过这个值了则不再回溯,类似于lru_cache的功能,
                # 当回溯函数第二次遇到相同的输入值时,直接返回之前记录的值大大节省了时间
                # 没有这个条件也能输出正确答案,只不过会超时
                elif memo[r1][c1][c2] is not None:
                    return memo[r1][c1][c2]
                else:
                    # 如果c1等于c2,有r1等于r2,则两个位置重合,则樱桃只能计算一次,此时c1 != c2 为0,乘以樱桃的数量后就只计算一次
                    # 否则如果位置不重合,则c1 != c2为真,此时要求两条路径的和
                    ans = grid[r1][c1] + (c1 != c2) * grid[r2][c2]
                    # 分四种情况进行回溯
                    ans += max(dp(r1, c1+1, c2+1), dp(r1+1, c1, c2+1),
                               dp(r1, c1+1, c2), dp(r1+1, c1, c2))
                # 每个四叉树回溯完都记录一次樱桃的个数,并返回,类似于576出界的路径数这道题中的方法,
                # 报错值的目的是方便遇到相同的输入时,直接返回值.
                memo[r1][c1][c2] = ans
                return ans
            dp(0,0,0)
            # return max(0, dp(0, 0, 0))
    if __name__ == '__main__':
        duixiang = Solution()
        a = duixiang.cherryPickup(  [[1,1,1,],
                                     [1,1,1,],
                                     [1,1,1,],
                                    ])
        # a = duixiang.cherryPickup( [[1,1,1,1,0,0,0],
        #                             [0,0,0,1,0,0,0],
        #                             [0,0,0,1,0,0,1],
        #                             [1,0,0,1,0,0,0],
        #                             [0,0,0,1,0,0,0],
        #                             [0,0,0,1,0,0,0],
        #                             [0,0,0,1,1,1,1]]
        # )
        print(a)
    View Code

    ttt

  • 相关阅读:
    MacOS如何正确配置Idea自带Maven插件的环境变量?(亲测)
    通过Canvas实现画板
    插入排序
    选择排序
    冒泡排序法
    进制
    JDBC(宠物管理系统)
    银行ATM存取款机系统MySQL数据库
    高级查询(二)
    高级查询
  • 原文地址:https://www.cnblogs.com/xxswkl/p/12123822.html
Copyright © 2020-2023  润新知