描述
给定一个数字三角形,找到从顶部到底部的最小路径和。每一步可以移动到下面一行的相邻数字上。
如果你只用额外空间复杂度O(n)的条件下完成可以获得加分,其中n是数字三角形的总行数。
您在真实的面试中是否遇到过这个题? 是
样例
比如,给出下列数字三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
从顶到底部的最小路径和为11 ( 2 + 3 + 5 + 1 = 11)。
兄弟们,今天我们就正式进入动态规划的环节了,动态规划很有意思,也很难,今天就简单介绍下思路。
我们在碰到一个题目的时候,首先要思考,这一题要用动态规划吗?
动态规划适用的三个场景:
1、求最大值最小值
2、判断是否可行
3、统计方案个数
动态规划不适用的三个场景:
1、求出所有具体方案而非方案个数
2、输入数据时一个集合(无序),而不是一个序列(有序)。
3、暴力算法的时间复杂度已经是多项式级别。动态规划擅长优化指数级别复杂度(2^n,n!)到多项式级别复杂度(n^2,n^3),不擅长优化从n^3到n^2。
那么动态规划的思路是什么呢?主要分四步:
1、状态 state
灵感,创造力,存储小规模问题的结果
2、方程 function
状态之间的联系,怎么通过小的状态,来算大的状态
3、初始化 initialization
最极限的小状态是什么,起点
4、答案 answer
最大的那个状态是什么,终点
OK,说完了动态规划,我们来看这一题。这一题很明显是动态规划每一个条件都符合,当然用dfs也可以做,但是如果用dfs做的话,时间复杂度是2^n。
这一题的思路是如何?对于一个结点来说,它的前序结点一定是它上一行的左边或者右边,要想让到这个结点的路径最小,那么到它的前序结点的路径也一定是最小的。
那么递推式已经出来了:每个结点的最短路径=min(左父结点最短路径,右父节点最短路径)+该结点本身权重
具体看代码,我注释写得很详细了:
class Solution:
"""
@param triangle: a list of lists of integers
@return: An integer, minimum path sum
"""
def minimumTotal(self, triangle):
# write your code here
if (len(triangle)==0 or triangle==None):return 0
dp=[]
for i in range(len(triangle)):
temp = []
for j in range(len(triangle[i])):
temp.append(0)
dp.append(temp)
dp[0][0]=triangle[0][0]
# dp数组中存的是每个坐标从顶往下的最短路径
# 每个数字的前一条路,要么从左上来,要么从右上来
# 左上坐标为row-1,column-1
# 右上坐标为row-1,column
# 每行第一列没有左上,只有右上
for j in range(1,len(triangle)):
dp[j][0]=dp[j-1][0]+triangle[j][0]
# 每行最后一列没有右上,只有左上
for j in range(1,len(triangle)):
dp[j][-1]=dp[j-1][-1]+triangle[j][-1]
for i in range(1,len(triangle)):
for j in range(1,len(triangle[i])-1):
# 存最短路径:每个坐标的最短路径=其前序结点的最短路径+该结点本身
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+triangle[i][j]
#找出最后一行中最小的
length=len(triangle)-1
minnum=99999999
for i in range(len(dp[length])):
if(dp[length][i]<minnum):
minnum=dp[length][i]
return minnum
s=Solution()
print(s.minimumTotal([
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]))