动态规划
Posted Hubery_Jun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划相关的知识,希望对你有一定的参考价值。
1. 斐波那契数列
斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。
示例 1:
输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2:
输入:n = 3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例 3:
输入:n = 4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3
提示:
0 <= n <= 30
题解:
class Solution:
def fib(self, n: int) -> int:
if n == 0:
return 0
if n == 1:
return 1
dp = [0] * (n + 1) # 定义 dp 数组
dp[0] = 0 # 初始化
dp[1] = 1
for i in range(2, n+1):
dp[i] = dp[i - 1] + dp[i - 2] # 递推公式
return dp[n]
2. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
提示:
1 <= n <= 45
爬楼梯实质上也可以看作一个斐波拉契数列:
class Solution:
def climbStairs(self, n: int) -> int:
dp = [0] * (n+1)
dp[0] = 1
dp[1] = 1
for i in range(2, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
类似题目 剑指 Offer 10- II. 青蛙跳台阶问题:
class Solution:
def numWays(self, n: int) -> int:
if n == 0 or n == 1:
return 1
dp = [0] * (n + 1)
dp[0] = 1 # 0 个台阶有 1 种 方法
dp[1] = 1 # 1 个台阶有 1 种方法
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n] % 1000000007
3. 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
示例 2:
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
示例 3:
输入:m = 7, n = 3
输出:28
示例 4:
输入:m = 3, n = 3
输出:6
提示:
1 <= m, n <= 100
题目数据保证答案小于等于 2 * 109
题解一:
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [[0 for i in range(n)] for j in range(m)]
def diff_path(row, col):
# 第一列的任意单元格,只有来自它上一个单元格过来的方法
for i in range(n):
dp[0][i] = 1
# 第一行的任意单元格,只有来自它前一个单元格过来的一种方法
for j in range(m):
dp[j][0] = 1
# 随意一个单元格有来自上或者左的两种路径,第一行、第一列已填充,不用继续填充
for i in range(1, row):
for j in range(1, col):
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
# 返回最后一个单元格的位置
return dp[row - 1][col - 1]
return diff_path(m, n)
题解二:(更容易理解)
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [[0] * n for i in range(m)]
for row in range(m):
for col in range(n):
# 第一个格子只有一种方法
if row == 0 and col == 0:
dp[row][col] = 1
elif col == 0:
# 第一行的任意单元格,只有来自它前一个单元格过来的一种方法
dp[row][col] = dp[row-1][col]
elif row == 0:
# 第一列的任意单元格,只有来自它上一个单元格过来的方法
dp[row][col] = dp[row][col - 1]
else:
# 其他情况(中间):任意一个单元格有来自其左或上两个方向的机器人
dp[row][col] = dp[row - 1][col] + dp[row][col - 1]
return dp[m - 1][n - 1]
4. 不同路径 II
示例 1:
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
示例 2:
输入:obstacleGrid = [[0,1],[0,0]]
输出:1
提示:
m == obstacleGrid.length
n == obstacleGrid[i].length
1 <= m, n <= 100
obstacleGrid[i][j] 为 0 或 1
题解:
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
row = len(obstacleGrid)
col = len(obstacleGrid[0])
# 只有一个单元格,即一行一列时
if row == 1 and col == 1:
if obstacleGrid[0][0] == 1:
return 0
else:
return 1
dp = [[0] * col for i in range(row)]
for i in range(row):
for j in range(col):
# 遇到阻碍,就跳过当前循环
if obstacleGrid[i][j] == 1:
continue
if i == 0 and j == 0:
dp[i][j] = 1
elif i == 0:
dp[i][j] = dp[i][j - 1]
elif j == 0:
dp[i][j] = dp[i - 1][j]
else:
dp[i][j] = dp[i][j - 1] + dp[i - 1][j]
return dp[row - 1][col - 1]
5. 最小路径和
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
示例 2:
输入:grid = [[1,2,3],[4,5,6]]
输出:12
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 200
0 <= grid[i][j] <= 100
题解:
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
# 只有一个单元格时
if m == 1 and n == 1:
return grid[0][0]
dp = [[0] * n for i in range(m)]
for i in range(m):
for j in range(n):
if i == 0:
# 第一行,和 = 当前单元格数字 + 前一个单元格数字
dp[i][j] = dp[i][j - 1] + grid[i][j]
elif j == 0:
# 第一列,和 = 当前单元格数字 + 上一个单元格数字
dp[i][j] = dp[i - 1][j] + grid[i][j]
else:
# 中间单元格,和 = 当前单元格数字 + 上一个和前一个单元格中最小的数字
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]
return dp[m-1][n-1]
以上是关于动态规划的主要内容,如果未能解决你的问题,请参考以下文章