前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >动态规划步步为营之[递推]->[记忆化]->[动态规划]

动态规划步步为营之[递推]->[记忆化]->[动态规划]

作者头像
公众号guangcity
发布2019-09-20 15:40:47
4000
发布2019-09-20 15:40:47
举报
文章被收录于专栏:光城(guangcity)光城(guangcity)

动态规划步步为营之[递推]->[记忆化]->[动态规划]

0.导语

今日步步为营,实战dp,采用递推、记忆化、动态规划,三种方法解决两道题目,并深入研究动态规划套路。

今日题目

  • 爬楼梯
  • 三角形最小路径和

1.爬楼梯

题目描述

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

代码语言:javascript
复制
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

示例 2:

代码语言:javascript
复制
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

思路

这里爬楼梯每次可以爬1个或2个台阶,也就是说当前的结果与前面和前面的前面有关,所以得到下面递推式:

代码语言:javascript
复制
res[i] = res[i - 1] + res[i - 2]

方法一:递归法

代码语言:javascript
复制
class Solution(object):
    def climbStairs(self,n):
        if n == 0 or n == 1:
            return 1
        else:
            return self.climbStairs(n - 1) + self.climbStairs(n - 2)

方法二:记忆化搜索

但是这样做存在着大量的重复运算。我们可以通过记忆化搜索的方式来优化上面的问题。

代码语言:javascript
复制
class Solution(object):
    def climbStairs(self,n):
        mem = [0]*(n+1)
        return self._climbStairs(n,mem)
    def _climbStairs(self,n,mem):
        if n==0 or n==1:
            return 1
        if mem[n]:
            return mem[n]
        mem[n] = self._climbStairs(n-1,mem)+self._climbStairs(n-2,mem)
        return mem[n]

方法三:动态规划

代码语言:javascript
复制
class Solution(object):
    def climbStairs(self,n):
        res = [0] * (n + 1)
        res[0] = 1
        res[1] = 1
        for i in range(2, n + 1):
            res[i] = res[i - 1] + res[i - 2]
        return res[n]

优化版

在Python中直接可以对两个数据进行交换,而不需要新增一个temp变量。

例如:

代码语言:javascript
复制
x, y = 1, 2
print(x, y)
x, y = y, x  # 交换同时进行
print(x, y)

上述的x,y=y,x中x与y同时进行!

代码语言:javascript
复制
def climbStairs2(n):
    x, y = 1, 1
    for _ in range(1, n):
        x, y = y, x + y
    return y

2.三角形最小路径和

题目描述

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

例如,给定三角形:

代码语言:javascript
复制
[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。

说明:

如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。

思路

这道题一开始采用了贪心策略,但是并不可行,因为这个只能求出局部最小,并不能求出全局最小,所以不符合题意。

下面给出贪心代码:

代码语言:javascript
复制
class Solution(object):
    def minimumTotal(self, triangle):
        res = 0
        row = len(triangle)
        t =  0
        for i in range(row):
            if i==0:
                res+=triangle[0][0]
            else:
                t1,t2 = t,t+1
                if triangle[i][t1]> triangle[i][t2]:
                    t=t2
                else:
                    t=t1
                res+=triangle[i][t]
        return res

假设当前行的坐标为(i,j),那么下一行相关的坐标为(i+1,j)与(i+1,j+1),也就是求这个相关坐标对应点的最小值,然后依次往下计算,得出一个局部最小路径,然后对比不同路径得到全局最优的最小路径,最后返回最小路径和即可。

代码语言:javascript
复制
原:
[
     [-1],
    [2,3],
   [1,-1,-3]
]
求和:
[
     [-1+min(1,0)=-1],
    [2+min(1,-1)=1,3+min(-1,-3)=0],
   [1,-1,-3]
]

由此图可知,自底向上计算,最终的第一个元素就是最后的最短路径。

递归法

递归法,首先确定终止条件,我们知道递归一轮后,会回到起点,那么当传入坐标(0,0)时候,递归终止条件就是行数超过指定行。然后依次递归回去往上计算。

代码语言:javascript
复制
class Solution:
    def minimumTotal(self, triangle):
        if not triangle:
            return 0
        return self._minimumTotal(0, 0, triangle)
    def _minimumTotal(self, row, col, triangle):
        if row>len(triangle)-1:
            return triangle[row][col]
           return triangle[row][col]+self._minimumTotal(row+1,col,triangle)+self._minimumTotal(row+1,col+1,triangle)

但是这样做存在着大量的重复运算。我们可以通过记忆化搜索的方式来优化上面的问题。

记忆化搜索

代码语言:javascript
复制
class Solution:
    def minimumTotal(self, triangle):
        if not triangle:
            return 0
        mem = [[0 for i in range(len(triangle[-1]))] for j in range(len(triangle))]

        return self._minimumTotal(0, 0, triangle,mem)

    def _minimumTotal(self, row, col, triangle,mem):
        if row + 1 == len(triangle):
            return triangle[row][col]
        if mem[row][col]:
            return mem[row][col]
        mem[row][col] = triangle[row][col] + min(self._minimumTotal(row + 1, col, triangle,mem), self._minimumTotal(row + 1, col + 1, triangle,mem))
        return mem[row][col]

动态规划

采用自底向上的动态规划方法,其中dp数组表示每次从末尾到(i,j)这个位置的最小路径和,到达顶端则为最终要求解的最短路径和!

第一种:开辟二维数组空间

代码语言:javascript
复制
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        row=len(triangle)
        col=len(triangle[-1])
        dp=[[0 for i in range(col)] for j in range(row)]
        for i in range(row-1,-1,-1):
            for j in range(len(triangle[i])):
                if i==row-1:
                    dp[i][j]=triangle[i][j]
                else:
                    dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j]
        return dp[0][0]

第二种:开辟一维数组空间

代码语言:javascript
复制
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        row=len(triangle)
        col=len(triangle[-1])
        dp=[0 for i in range(row*col)]
        for i in range(row-1,-1,-1):
            for j in range(len(triangle[i])):
                if i==row-1:
                    dp[i*col+j]=triangle[i][j]
                else:
                    dp[i*col+j]=min(dp[(i+1)*col+j],dp[(i+1)*col+j+1])+triangle[i][j]

        return dp[0]

上述优化版:

考虑到dp二维数组中存放这的是从末尾到某一位置的最短路径和,那么当当前行操作完毕,此数据就可以通过在下一行做修改,也就是说利用这个特性,那么就直接转为一位数组即可解决,不断修改某个重复修改。

代码语言:javascript
复制
class Solution(object):
    def minimumTotal(self, triangle):
        mini = triangle[-1]
        for row in range(len(triangle) - 2, -1, -1):
            for j in range(len(triangle[row])):
                mini[j] = min(mini[j], mini[j + 1]) + triangle[row][j]
        return mini[0]

第三种:原地修改

代码语言:javascript
复制
class Solution(object):
    def minimumTotal(self, triangle):
        for row in range(len(triangle) - 2, -1, -1):
            for j in range(len(triangle[row])):
                triangle[row][j] = min(triangle[row + 1][j], triangle[row + 1][j + 1]) + triangle[row][j]

        return triangle[0][0]

这个原地修改真的非常棒!一开始最后一行所有数据不变,我们就想要最后一行的原数据存储到我们的dp数组中,后面我们就是不断修改对应(i,j)的位置,因为是从底向上的,也就是每一行的每一列数据它只修改一次,所以直接使用原数组即可,空间复杂度达到了O(1)!

从上面两道题我们又深入分析了动态规划的学习,从递推到记忆化,再到必杀动态规划!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 动态规划步步为营之[递推]->[记忆化]->[动态规划]
    • 0.导语
      • 1.爬楼梯
        • 2.三角形最小路径和
        相关产品与服务
        数据保险箱
        数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档