好理解的DP系列 Leetcode62.不同路径+Leetcode64.最小路径和
一、不同路径
思路:
用f(i,j)表示从左上角走到(i,j)的路径数量,其中i和j的范围分别是[0,m)和[0,n)。
由于我们每一步只能向下或者向右移动一步,因此如果我们想要移动到(i,j),如果向下走一步,会从(i-1,j)移动过来,如果向右走一步,会从(i,j-1)移动过来,因此我们可以得到状态转移方程:f(i,j)=f(i-1,j)+f(i,j-1)
- 注意: 如果i=0或者j=0,那么f(i-1,j)并不满足状态转移方程,应忽略
- base case: f(0,0)=1,即从左上角走到左上角只有一种方法
- 最终答案: f(m-1,n-1)
- 细节:为了避免i=0或者j=0的时刻,我们可以将所有的f(0,j)以及f(i,0)都设置为边界条件,值为1
那么如何构建一个设置边界条件的二维列表呢?
例如:此时m=3,n=7
以下代码就可以实现,说实话,我真没想出来,感觉自己蠢爆了,看了题解才明白可以这么写,对于相同的大于2的维度的列表,可以直接相加,相当于作为了一行
dp= [[1]*n]+[[1]+[0]*(n-1) for _ in range(m-1)]
之后就是构建dp_table了,此时状态转移方程是:dp[i][j]=dp[i-1]+dp[i][j]
,这么一想是不是和青蛙跳台阶差不多,注意此时i和j的取值是从1开始的,i=0或j=0的时刻我们都已经特殊考虑了,返回的是dp[m-1][n-1]实现的代码如下:
def uniquePaths(m: int, n: int) -> int:
f = [[1]*n]+[[1]+[0]*(n-1) for _ in range(m-1)]
print(f)
for i in range(1,m):
for j in range(1,n):
f[i][j] = f[i-1][j]+f[i][j-1]
return f[m-1][n-1]
也可以直接构建dp列表,将所有特殊的情况使用if elif else表示出来!实现代码如下:
def uniquePaths(m: int, n: int) -> int:
# 直接构建dp_table
dp = [[0]*n for _ in range(m)] # 先随便构建一个二维列表
for i in range(m):
for j in range(n):
if i==0 and j==0:# 左上角起点值
dp[i][j]=1
elif i==0: # 第一行
dp[i][j]=1
elif j==0: # 第一列
dp[i][j]=1
else:
dp[i][j] = dp[i-1][j]+dp[i][j-1]
return dp[m-1][n-1]
二、最短路径(最小路径和)
如果了解了不同路径的做法,这道题也就迎刃而解了,上一道题dp[i][j]代表了到i,j位置总共的路径个数,那么这道题我们可以设dp[i][j]是到达i,j位置的最小路径和,那么如何计算呢,不难发现,i,j位置的最小路径和就是可以转移过来的两个状态中的小的那个与现在位置的值的和。
直接上代码, ps:受上一道题先构建边界条件的dp矩阵的思路影响,我的思路直接固定了,非要先实现一个第一行和第一列是边界条件的矩阵,这也太难了,因为不能用numpy,这样很难实现呀,为什么不直接在code里把特殊情况直接考虑进去呢?
def minPathSum(grid: List[List[int]]) -> int:
for i in range(len(grid)):
for j in range(len(grid[0])):
if i==0 and j==0:
continue
elif i==0: grid[i][j] = grid[i][j]+ grid[i][j-1]
elif j==0: grid[i][j] = grid[i][j]+grid[i-1][j]
else:
grid[i][j] = min(grid[i-1][j],grid[i][j-1])+grid[i][j]
return grid[len(grid)-1][len(grid[0])-1]