动态规划题目总结
近日做了一些LeetCode中的动态规划的题目,可以做个总结了!
1:322.零钱兑换
2:121.买卖股票最佳时机I
3:122.买卖股票的最佳时机II
4:面试题08.01三步问题
5:70.爬楼梯
6:746.使用最小路径爬楼梯
7:343.整数拆分
8:62.不同路径
9:63.不同路径II
322.零钱兑换
该题显然是可以用动态规划去做的。不明白的可以看看这篇文章。里面列出了哪些情况是可以用动态规划去做。
动态规划的做题步奏如下
1:确定dp矩阵,并明确dp[i]或者其他形式,但一定要理解其含义是什么,比如此题dp[i]的意思
就是拼出价值为i需要的最少硬币数目
dp = [sys.maxsize-1] * (amount+1)。对dp矩阵每一个值都初始化为无穷大
为什么是amount+1?因为需要一个dp[0]去存储初始化的值
2:*初始化*,需要对dp矩阵进行初始化,个人理解是动态规划很类似于递归,需要给一个出口,不然
就一直死循环下去。
dp[0] = 0,即拼出0元需要最少的硬币数是0
3:*转移方程*,也是最重要的。
这题中没有给固定的coins,就拿第一个example来举例吧。
coins= 【1,2,5】,对dp矩阵每个值进行遍历赋值,dp[i]的值就是dp[i]+1,即加上当前的coin和
dp[coin+i]进行比较哪个比较小。因为计算顺序是从左往右,所以当计算后面i值时候,前面已经有结果了。
dp[i+coin] = min(dp[coin+i],dp[i]+1)
dp = [sys.maxsize-1] * (amount+1)#定义一个dp矩阵,分别存放着拼出dp[i]所需最少的硬币个数
dp[0] = 0 #初始化dp矩阵最开始的元素为0
for i in range(amount+1):#初始化之后开始最dp数组中每个元素进行赋值
if dp[i] == sys.maxsize - 1:#如果dp[i]当前元素为无穷,则说明此时无解,进行下轮循环
continue
for coin in coins:#若此时dp[i]不是无穷大,则分别添加硬币列表中的硬币进行选择
if coin + i <= amount:#如果加上此时的硬币还小于amount
dp[i+coin] = min(dp[coin+i],dp[i]+1)#比较原始的跟加上一枚之后哪个更小
if dp[-1] == sys.maxsize -1:return -1#如果循环赋值之后,dp[i]最后一位还是无穷,则无解
else:return dp[-1]#有解,则返回最后一个数
121.买卖股票最佳时机I
解题思路:
按照正常的思想肯定是在最低价的时候买入,然后在最高价的时候卖出,这样才能使获得的利润最大。
可以设置一个变量minprice用来存储当前所遇到的最低的价格,然后判断当天是卖出股票所能获得的利润大还是不卖获得的利润大。
动态规划解题步奏:
1:确定dp矩阵,并明确dp[i]所代表的含义:
dp[i]:代表着前i天操作股票可以获得的最大收益
2:初始化dp矩阵:
dp[0] = 0
3:状态转移方程
dp[i] = max( dp[i-1] ,prices[i])-minprice)
即比较在第i天不卖出股票和根据当天的价格卖出股票,哪个可以获得的收益更大
python代码如下
def maxProfit(self, prices: List[int]) -> int:
n = len(prices) #价格数组的长度
if n == 0:return 0
dp = [0] * n #初始化dp矩阵
#dp[i]表示前i天可以获得的最大利润
minprice = prices[0]#要找到卖股票的最低价
for i in range(1,len(prices)):#从第二个价格开始遍历
minprice = min(minprice,prices[i]) #比较当前价格和当前最低价哪个更小
dp[i] = max(dp[i-1],prices[i] - minprice)
#比较不卖或者卖出哪个可以获得更大的收益
return dp[n-1]
122.买卖股票的最佳时机II
这题其实可以不用动态规划去做。代码如下很简单。
def maxProfit(self, prices: List[int]) -> int:
pro = 0 #可以获得总利润
for i in range(1,len(prices)):
tmp = prices[i] - prices[i-1]#每天都进行交易
if tmp > 0:#如果是赚钱,则加到总利润中
pro += tmp
return pro
面试题08.01三步问题
if n == 1:
return 1
elif n == 2:
return 2
elif n == 3:
return 4
tmp1,tmp2,tmp3 = 1,2,4
for i in range(3,n):
tmp1,tmp2,tmp3 = tmp2,tmp3,tmp1+tmp2+tmp3
tmp1 = tmp1 % 1000000007
tmp2 = tmp2 % 1000000007
tmp3 = tmp3 % 1000000007
return tmp3
70.爬楼梯
dp ={
}#dp矩阵
dp[1]=1
dp[2]=2
for i in range(3,n+1):
dp[i] = dp[i-1] + dp[i-2] #动态转移方程
return dp[n]
很简单的一个动态规划的题目
746.使用最小路径爬楼梯
n = len(cost)
dp = [sys.maxsize] * (n+1) #初始化dp数组 dp【i】代表第i步的花费
dp[0] = cost[0] #初始化
dp[1] = cost[1] #初始化
for i in range(2,n):
dp[i] = min(dp[i-1],dp[i-2]) + cost[i] #转移方程
return min(dp[n-1],dp[n-2])
343.整数拆分
dp = [0] * (n+1) #初始化dp数组
dp[2] = 1 #初始化dp[2]=1
for i in range(3,n+1): #从第三个数开始赋值运算
for j in range(1,i): #拆解i,从1一直到i-1
dp[i] = max(dp[i],max(j * (i-j),j * dp[i-j]))
return dp[-1]
62.不同路径
def uniquePaths(self, m: int, n: int) -> int:
#初始化二维矩阵,左边和右上都初始化为1
dp = [[1] * n] + [[1] + [0] * (n-1) for _ in range(m-1)]
for i in range(1,m):
for j in range(1,n):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
63.不同路径II
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
row = len(obstacleGrid) #矩阵的行数
column = len(obstacleGrid[0]) #矩阵的列数
dp = [[0] * column for i in range(row)] #新建一个全是0的矩阵
#下面是对矩阵的左边框和上边框赋值1 ,因为只有向右或者向下一种走法
for i in range(row):
if obstacleGrid[i][0] == 1:#如果当前不是障碍物
break
dp[i][0] = 1
for j in range(column):
if obstacleGrid[0][j] == 1:#如果当前不是障碍物
break
dp[0][j] = 1
for x in range(1,row): #从行开始循环
for y in range(1,column): #从列开始循环
if obstacleGrid[x][y] == 1: #遇到障碍物则退出
continue
dp[x][y] = dp[x-1][y] + dp[x][y-1] #否则进行动态规划计算
return dp[row-1][column-1]