出界的路径数
题目:给定一个 m × n 的网格和一个球。球的起始坐标为 (i,j) ,你可以将球移到相邻的单元格内,或者往上、下、左、右四个方向上移动使球穿过网格边界。但是,你最多可以移动 N 次。找出可以将球移出边界的路径数量。答案可能非常大,返回 结果 mod 10^9 + 7 的值。
方法一
一开始看题,以为类似机器人路径问题,直接用dfs迭代N次,从起点开始,将可到达的位置入栈,判断当前位置是否出界,若出界,路径数量加一;反之,将它下一步可到达的位置入栈。
但是本题与以往类似(做过)的题目有些不同,本题的路径是带返回的,也就是说,某个点可以重复多次被访问。这样下来,重复计算的量就很庞大。(其实从题目中返回结果mod 10 ^ 9 + 7就看出来,计算量不小哇。)
方法二
由于方法一超时,必须想到一种带记忆的算法解决本题有大量重复计算的特点。那么,动态规划它来啦!
因为球的每一次移动都是由上一次移动的结果决定的,通过累加每一次移动后,球处于边界位置的状态数(当球处于边界位置的时候,下次一移动一定可以出界)并且乘以该边界位置可从几个方向出界(cnt<=3)。这样可以得到最终的球出界的路径数。
其中,动态转移方程为:
dp[i][j][k] = dp[i-1][j][k-1] + dp[i+1][j][k-1] + dp[i][j-1][k-1] + dp[i][j+1][k-1] ,k为移动次数。
这里初始化问题,为了简便,省去讨论边界问题,初始化的矩阵要大给定矩阵一圈。这样,在更新每个节点的值的时候,不用判断当前横、纵坐标是否越界。
class Solution(object):
def findPaths(self, m, n, N, i, j):
"""
:type m: int
:type n: int
:type N: int
:type i: int
:type j: int
:rtype: int
"""
lis = [[[0 for p in range(n+2)] for q in range(m+2)]]
lis[0][i+1][j+1] = 1 #初始化,第0次移动起始节点值为1
ans = 0 #路径个数
k = 0 #移动次数
while k < N:
temp = [[0 for x in range(n+2)] for y in range(m+2)]
for p in range(1,m+1):
for q in range(1,n+1):
if lis[k][p][q] > 0:
cnt = 0 #计算可出界的方向数
if p == 1:
cnt += 1
if p == m:
cnt += 1
if q == 1:
cnt += 1
if q == n:
cnt += 1
ans += cnt * lis[k][p][q]
temp[p][q] = lis[k][p-1][q] + lis[k][p+1][q] + lis[k][p][q-1] + lis[k][p][q+1]
lis.append(temp)
k += 1
return ans % (10 ** 9 + 7)