动态规划-Dynamic Programming(DP)

动态规划

动态规划方法心得

? 动态规划是一般的面试、笔试中的高频算法题,熟练掌握必要的。动态规划的中心思想是在解决当前问题时,可以由之前已经计算所得的结果并结合现在的限制条件递推出结果。由于此前的计算结果已经保留下来,所以极大的缩短了时间复杂度。

? 解决动态规划问题的关键是找出状态表达式,即如何由之前的结果推导出现在的结果。另外,有的问题有很多限制条件增加问题的难度,需要剥丝抽茧,将问题解决。在找到状态表达式后,分为三步解决问题:

一. 定义内存空间,用来保存每步结果,并根据题目初始化,有些简单的题目只与前一步相关就不需要定义内存。

二. 根据状态转移表达式依次计算每步的结果,这里需要注意各种限制条件。

三. 将内存中的数据根据要求作为问题的结果返回。

动态规划题目总结

5. 最长回文子串

题目描述: 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

解题思路:动态规划最重要的就是找到状态转移矩阵,此题进行二层循环遍历是否是回文字符串,首先定义一个全为False的二维数组,当前遍历的的结果取决于收尾是否相等和除去收尾的字符串是否为回文字符串两个条件,由此可得状态转移矩阵为:\(dp[l, r] = (s[l] == s[r] and (r - l <= 2 or dp[l + 1, r - 1]))\)。

代码

class Solution:
    def longestPalindrome(self, s: str) -> str:
        size = len(s)
        if size <= 1:
            return s
        # 二维 dp 问题
        # 状态:dp[l,r]: s[l:r] 包括 l,r ,表示的字符串是不是回文串
        # 定义二维列表,并规定全部取值为False,这也是后面为什么没有输出False的原因
        dp = [[False for _ in range(size)] for _ in range(size)]

        longest_l = 1
        res = s[0]

        # 因为只有 1 个字符的情况在最开始做了判断
        # 左边界一定要比右边界小,因此右边界从 1 开始
        for r in range(1, size):
            for l in range(r):
                # 状态转移方程:如果头尾字符相等并且中间也是回文
                # 在头尾字符相等的前提下,如果收缩以后不构成区间(最多只有 1 个元素),直接返回 True 即可
                # 否则要继续看收缩以后的区间的回文性
                # 重点理解 or 的短路性质在这里的作用
                if s[l] == s[r] and (r - l <= 2 or dp[l + 1][r - 1]):
                    dp[l][r] = True
                    cur_len = r - l + 1
                    if cur_len > longest_l:
                        longest_l = cur_len
                        res = s[l:r + 1]
        return res

10. 正则表达式匹配

32. 最长有效括号

44. 通配符匹配

53. 最大子序和

题目描述: 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

解题思路:思路一:首先,使用第一个数初始化最大连续数组和;然后遍历数组,并依次相加;当超过之前的最大值时就替换,当小于0时就重新初始化为0。思路二:动态规划。遍历数组,状态转移矩阵为:dp[i] = dp[i-1] + dp[i],不过如思路一相同,当前面连续和大于0时就继续加上当前的值,当小于0时就重新计算。此题由于只与前一结果关就没有单独定义列表去储存结果。

思路一:

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        max_val, cur = nums[0], 0
        for x in nums:
            cur += x
            if cur > max_val:
                max_val = cur
            if cur < 0:
                cur = 0
        return max_val

思路二:

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        max_sum = nums[0]
        for i in range(1, n):
            if nums[i - 1] > 0:
                nums[i] += nums[i - 1]
            max_sum = max(nums[i], max_sum)

        return max_sum

62. 不同路径

题目描述:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。问总共有多少条不同的路径?

解题思路:典型的动态规划题目,首先创建存放结果的列表,然后依次由已知的第一行和第一列循环计算,状态转移矩阵为:dp[i, j] = dp[i-1, j] + dp[i, j-1],最后输出d[-1, -1]。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        # 动态规划
        # 定义二维空列表
        dp = [[0 for i in range (n+1)] for i in range(m+1)]
        # 初始化第一行列表的值
        for i in range(1, n+1):
            dp[1][i] = 1
        # 由状态转移矩阵求解列表其余的值
        for i in range(2, m+1):
            for j in range(1, n+1):
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
        # 返回最后的值
        return dp[m][n]

63. 不同路径II

题目描述:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

解题思路:与上题的解题思路相同,不同的是当遇到障碍物时,列表的值不是由状态转移矩阵计算而来,而是直接设为0。

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        # 同样是动态规划,不过在障碍处标记为0,并且不进行遍历;
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        dp = [[0 for i in range(n+1)] for j in range(m+1)]
        for i in range(1, n+1):
            if obstacleGrid[0][i-1] == 1:
                break
            else:
                dp[1][i] = 1

        for i in range(2,m+1):
            for j in range(1,n+1):
                if obstacleGrid[i-1][j-1]:
                    dp[i][j] = 0
                else:
                    dp[i][j]=dp[i-1][j]+dp[i][j-1]
        return dp[m][n]

64. 最小路径和

题目描述:给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。说明:每次只能向下或者向右移动一步

解题思路:典型动态规划题,老套路,定义二维列表,初始化第一行和第一列的值,遍历列表,根据状态转移矩阵dp[i, j] = min(dp[i, j-1], dp[i-1, j]) + grid[i, j]

class Solution(object):
    def minPathSum(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        m , n = len(grid) , len(grid[0])
        dp = [[0x7fffffff for x in range(n+1)] for x in range(m+1)]
        dp[0][1] = dp[1][0] = 0
        for i in range(1,m+1):
            for j in range(1,n+1):
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i-1][j-1]
        return dp[m][n]

70. 爬楼梯

题目描述:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

解题思路:典型动态规划问题,状态转移矩阵:dp[i] =dp[i-1] + dp[i-2]。于此相同的就是斐波那契数列。

class Solution:
    def climbStairs(self, n: int) -> int:
        if n == 1:
            return 1
        elif n == 2:
            return 2
        else:
            a, b =1, 2
            for i in range(n-2):
                c = a + b
                a = b
                b = c
            return c

91. 解码方法

题目描述: 一条包含字母 A-Z 的消息通过以下方式进行了编码:

'A' -> 1
'B' -> 2
...
'Z' -> 26

给定一个只包含数字的非空字符串,请计算解码方法的总数。

解题思路:此题和爬楼梯问题类似,不一样的是增加了很多限制条件。(1)最后两位数是否可以解码,也就是是否在1~26之间;(2)只对最后一位进行解码时,最后一位数是否为0;

class Solution:
    def numDecodings(self, s: str) -> int:
        if not s: return 0
        n = len(s)
        dp = [0] * n
        dp[0] = 1 if s[0] != '0' else 0
        for i in range(1, n):
            if 10 <= int(s[i - 1:i + 1]) <= 26:
                dp[i] += dp[i - 2] if i >= 2 else 1
            if s[i] != '0':
                dp[i] += dp[i - 1]
        return dp[n - 1]

120. 三角形最小路径和

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

例如,给定三角形:

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

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

解题思路:

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

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        mini, M = triangle[-1], len(triangle)
        for i in range(M - 2, -1, -1):
            for j in range(len(triangle[i])):
                mini[j] = triangle[i][j] + min(mini[j], mini[j+1])
        return mini[0]

原文地址:https://www.cnblogs.com/ffjsls/p/12121510.html

时间: 2024-10-08 16:38:57

动态规划-Dynamic Programming(DP)的相关文章

算法应用公式(二)动态规划 Dynamic Programming

动态规划在计算机中是一个比较玄学的算法,有的人可能看很久都很疑惑这到底是怎么回事,但是一旦理解了,上手就非常容易了. 算法描述 (以下内容来自百度百科)动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法.20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimali

动态规划 Dynamic Programming

March 26, 2013 作者:Hawstein 出处:http://hawstein.com/posts/dp-novice-to-advanced.html 声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处. 前言 本文翻译自TopCoder上的一篇文章: Dynamic Programming: From novice to advanced ,并非严格逐字逐句翻译,其中加入了自己的

优化问题 Optimization Problems &amp; 动态规划 Dynamic Programming

2018-01-12 22:50:06 一.优化问题 优化问题用数学的角度来分析就是去求一个函数或者说方程的极大值或者极小值,通常这种优化问题是有约束条件的,所以也被称为约束优化问题. 约束优化问题(亦译为受约束的最优化问题)是一类数学最优化问题,它由目标函数以及与目标函数中的变量相关的约束条件两部分组成,优化过程则为在约束条件下最优化(最大化或最小化)目标函数. 经典的优化问题: 最短路问题 旅行商问题(TSP) 装箱问题 调度问题 背包问题 了解并熟练掌握这些经典的优化问题会对以后遇到的新的

Dynamic Programming (DP) 问题总结

所有的 DP 问题都可以简单得用 Recursion 的方式实现.这通常是最容易想到的思路. 问题是这种实现不够 efficient,存在 subproblem 被重复计算的情况.有两种解决这个问题的方法: 1. 很直观的,在 naive recursion 里加入 一个 save 的环境,把每个 subproblem 计算出的值存起来.这种方式也叫 Top-down approach. 2. Bottom-up approach: 上面的方法的思路是从大问题开始,计算的时候发现需要小问题的解,

hdu 4223 Dynamic Programming? (dp)

//连续的和的绝对值最小 # include <stdio.h> # include <string.h> # include <algorithm> # include <math.h> using namespace std; int main() { int t,i,j,num,n,min1; int sum[1010],a[1010]; int cas=0; while(~scanf("%d",&t)) { while(t

Dynamic Programming 动态规划

Dynamic Programming DP 的历史渊源: Richard E. Bellman (1920-1984) Richard Bellman received the IEEE Medal of Honor, 1979. "Bellman . . . explained that he invented the name 'dynamid programming' to hide the fact that he was doing mathe-matical research at

Speeding Up The Traveling Salesman Using Dynamic Programming

转自:https://medium.com/basecs/speeding-up-the-traveling-salesman-using-dynamic-programming-b76d7552e8dd Using dynamic programming to speed up the traveling salesman problem! A large part of what makes computer science hard is that it can be hard to kn

动态规划算法(Dynamic Programming,简称 DP)

动态规划算法(Dynamic Programming,简称 DP)似乎是一种很高深莫测的算法,你会在一些面试或算法书籍的高级技巧部分看到相关内容,什么状态转移方程,重叠子问题,最优子结构等高大上的词汇也可能让你望而却步. 而且,当你去看用动态规划解决某个问题的代码时,你会觉得这样解决问题竟然如此巧妙,但却难以理解,你可能惊讶于人家是怎么想到这种解法的. 实际上,动态规划是一种常见的「算法设计技巧」,并没有什么高深莫测,至于各种高大上的术语,那是吓唬别人用的,只要你亲自体验几把,这些名词的含义其实

Dynamic programming:from novice to advanced【动态规划】

动态规划:从新手到专家 出处:http://hawstein.com/posts/dp-novice-to-advanced.html 声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处. 前言 本文翻译自TopCoder上的一篇文章: Dynamic Programming: From novice to advanced ,并非严格逐字逐句翻译,其中加入了自己的一些理解.水平有限,还望指摘.