题目地址:跳跃
此题是一道比较经典又带有一定难度的动态规划题目,且听我慢慢道来(虽然不一定能讲明白:( )
先输入数据:
n, m = map(int, input().split())
score = []
for i in range(n):
score.append(list(map(int, input().split())))
动态规划第一步,动态规划必要dp数组,我们先确定dp数组的含义,此题目要求求出权的最大值,那我们dp数组的含义便是所含最大权和,那么dp[n-1][m-1]便是我们要找的答案。
动态规划第二部,也是最难的一步,便是确认dp数组,一旦确认的dp数组,代码和思路必然会好写很多,对于这道题,我们可以采用自顶向下的备忘录递归解法,在此,我们采用自底向上的动态规划解法。对于一般动态规划问题,我们经常采用先假设一个中间常量,然后思考如何才能到达这个中间项,以此题为例,假设我们在求解过程中到达了一个点dp[i][j],我们如何才能到达这个点呢?以下图我们可以知道,黑色为假设点,其周围绿色的是可以到达假设点的所有点,而我们的目的就是求出权的最大值,所以dp方程的大体思路我们就知道了,dp[i][j] = max(dp[i][j],???),对于???来说,我们知道要求最大值,但是如何取到呢?我们可以采用设置jump数组来表示点的移动,所以dp方程为:dp[i][j] = max(dp[x][y], dp[i][j])
设立jump数组:
jump = [[0, -1], [0, -2], [0, -3], [-1, 0], [-2, 0], [-3, 0], [-1, -1], [-1, -2], [-2, -1]] # 表示移动方向
用for循环来实现点的移动:
for dx, dy in jump:
x = i + dx
y = j + dy
动态规划第三步,初始化数据,因为dp数组中我们存在max()函数,所以我们先把dp数组值取最小,对于dp[0][0]来说,他就是初始点,没有任何一个点能从别的点到达此点,所以其值为初始值。
初始化:
dp = [[-99999] * m for _ in range(n)]
dp[0][0] = score[0][0]
注意设立二维数组时不能以以下语句:
dp = [[-99999] * n] * m
这样设置会导致你变化其中一行时,所有行都会变化,因为他们的地址是一样的。
完整代码:
n, m = map(int, input().split())
score = []
jump = [[0, -1], [0, -2], [0, -3], [-1, 0], [-2, 0], [-3, 0], [-1, -1], [-1, -2], [-2, -1]] # 表示移动步数
for i in range(n):
score.append(list(map(int, input().split())))
dp = [[-99999] * m for _ in range(n)]
dp[0][0] = score[0][0]
for i in range(n):
for j in range(m):
if i == j == 0: # 起始点,已经不能再上下移动了
continue
for dx, dy in jump: # 开始索引最大权值
x = i + dx # i不能变,因为i还要参与循环
y = j + dy
if x >= 0 and x < n and y >= 0 and y < m: # 跳跃过程中不能越界
dp[i][j] = max(dp[x][y], dp[i][j]) # dp方程
dp[i][j] += score[i][j] # 将最大的权值加上
print(dp[n - 1][m - 1])