题目描述
小蓝在一个 n 行 m 列的方格图中玩一个游戏。
开始时,小蓝站在方格图的左上角,即第 1 行第 1 列。
小蓝可以在方格图上走动,走动时,如果当前在第 r 行第 c 列,他不能走到行号比 r 小的行,也不能走到列号比 c 小的列。同时,他一步走的直线距离不超过 3。
例如,如果当前小蓝在第 3行第 5 列,他下一步可以走到第 3 行第 6 列、第 3 行第 7 列、第 3 行第 8列、第 4 行第 5 列、第 4 行第 6 列、第 4 行第 7 列、第 5 行第 5 列、第 5 行第 6 列、第 6 行第 5 列之一。
小蓝最终要走到第 n 行第 m 列。
在图中,有的位置有奖励,走上去即可获得,有的位置有惩罚,走上去就要接受惩罚。奖励和惩罚最终抽象成一个权值,奖励为正,惩罚为负。
小蓝希望,从第 1 行第 1列走到第 n 行第 m 列后,总的权值和最大。请问最大是多少?
输入描述
输入的第一行包含两个整数n,m,表示图的大小。
接下来 n 行,每行 m个整数,表示方格图中每个点的权值。
其中,1 <=n <= 100,-10^4 <=权值 <=10^4。
输出描述
输出一个整数,表示最大权值和。
输入输出样例
示例 1
输入
3 5
-4 -5 -10 -3 1
7 5 -9 3 -10
10 -2 6 -10 -4
输出
15
解题思路
本题是模拟赛的一道dp题,要求小蓝从1行1列到n行m列后的最大总权值和。
那我们先来看小蓝每一步可以怎么走,因为一步走的直线距离不能超过3,如图所示:
有一个5行5列的图,小蓝站在(1,1)位置上,下一步可以走到的点就是蓝色部分,那么选择走到哪个点呢?因为题目要求总的权值和最大,所以我们就选权值最大的那个点作为下一个位置。这里我们已经知道了小蓝是怎么选择走到下一个位置的,那么问题来了,当小蓝走到(5,5)也就是出口的时候,怎么算总的权值呢?这里我们就要换个思路了,如下图:
当小蓝站在(4,4)位置上时,可以从哪些点走到小蓝的位置?很明显蓝色部分就是可以走到小蓝的位置,和上面不同的是,这里我们选择权值最大的点来作为小蓝的上一个位置,以此类推,当小蓝走到(5,5)的时候dp[-1][-1]就是我们要的结果。
所以当前可确定状态转移方程为:
dp[i][j]+=max(dp[i][j-1],dp[i][j-2],dp[i][j-3],dp[i-1][j],dp[i-1][j-1],dp[i-1][j-2],dp[i-2][j],dp[i-2][j-1],dp[i-3][j])
但要注意的是,不是所有的位置都是可以从9个位置选择一个权值最大的作为上一位置的。比如小蓝站在(2,2)的位置,那么他的上一位置就只有从(1,1),(1,2),(2,1)三个选择权值最大的一个,所以我们还需要判断当前位置的上一位置是否还在图中,也就是有没有出边界。所以为了更好的判断,我们可以设一个列表来存储增量,通过遍历增量来判断上一位置的索引:
dirs=[(0,1),(0,2),(0,3),(1,0),(1,1),(1,2),(2,0),(2,1),(3,0)] #增量
我们通过当前位置和依次遍历增量来判断所有满足上一位置的点,如果这个点满足的话那么我们就把这个点的权值保存到一个列表result中,直到我们把所有满足上一位置的点的权值都保存到列表之后,我们加上最大的一个就行了。这时新的状态转移方程为:
dp[i][j]+=max(result)
当然如果当前位置是起始位置(0,0)时,这时候它就不能满足转移方程 ,所以这里我们还要加一个判断是不是在起始位置。
Code:
n,m=map(int,input().split())
dp=[]
for i in range(n):
l=list(map(int,input().split()))
dp.append(l) #接收数据
dirs=[(0,1),(0,2),(0,3),(1,0),(1,1),(1,2),(2,0),(2,1),(3,0)] #增量
for i in range(n):
for j in range(m):
result=[]
for x,y in dirs: #根据当前位置和增量判断上一位置
x1=i-x #上一位置的横轴索引
y1=j-y #上一位置的纵轴索引
if x1>=0 and x1<n and y1>=0 and y1<m: #判断上一位置是否在图中
res.append(dp[x1][y1]) #在的话把权值添加进列表
if i==0 and j==0: # 在(0,0)位置跳过
continue
else:
dp[i][j]+=max(result) #转移方程,加上最大的权值
print(dp[-1][-1]