题目:
给你一个大小为 m x n 的网格和一个球。球的起始坐标为 [startRow, startColumn] 。你可以将球移到在四个方向上相邻的单元格内(可以穿过网格边界到达网格之外)。你 最多 可以移动 maxMove 次球。
给你五个整数 m、n、maxMove、startRow 以及 startColumn ,找出并返回可以将球移出边界的路径数量。因为答案可能非常大,返回对 109 + 7 取余 后的结果。
算法:
方法一:记忆化搜索+DFS
func findPaths(m int, n int, maxMove int, startRow int, startColumn int) int {
dp := make([][]int, m)
for i := range dp {
dp[i] = make([]int, m)
}
modVal := 1000000000 + 7
cache := make(map[int]int, 0)
var dfs func (i, j, k int) int
// 执行了很多重复的dfs(i,j,k),所以将三个参数i,j,k记忆化
dfs = func(i, j, k int) int {
if k < 0 {
return 0
}
key := i * 2500 + j * 50 + k
if val, ok := cache[key]; ok {
return val
}
if i < 0 || j < 0 || i == m ||j == n{
return 1
}
cnt := 0
cnt = (cnt + dfs(i - 1, j, k - 1)) % modVal
cnt = (cnt + dfs(i + 1, j, k - 1)) % modVal
cnt = (cnt + dfs(i, j - 1, k - 1)) % modVal
cnt = (cnt + dfs(i, j + 1, k - 1)) % modVal
cache[key] = cnt % modVal
return cnt
}
return dfs(startRow, startColumn, maxMove)
}
方法二:动态规划
func findPaths(m int, n int, maxMove int, startRow int, startColumn int) int {
// dp[k][i][j] maxMove=k,坐标为(i,j)的路径数量
// dp[k][i][j] = dp[k - 1][i - 1][j] + dp[k - 1][i + 1][j]
// + dp[k - 1][i][j - 1] + dp[k - 1][i][j + 1] k > 0时
dp := make([][][]int, maxMove + 1)
for k := range dp {
dp[k] = make([][]int, m)
for i := range dp[k] {
dp[k][i] = make([]int, n)
}
}
mod := 1000000007
ans := 0
dicts := [][]int{[]int{-1,0},[]int{1,0},[]int{0,-1},[]int{0,1}}
dp[0][startRow][startColumn] = 1
for k := 0; k < maxMove; k ++ {
for i := 0; i < m; i ++ {
for j := 0; j < n; j ++ {
count := dp[k][i][j]
if count > 0 {
for _, dict := range dicts {
i1 := dict[0] + i
j1 := dict[1] + j
if 0 <= i1 && i1 < m && 0 <= j1 && j1 < n {
dp[k + 1][i1][j1] = (dp[k + 1][i1][j1] + count) % mod
} else {
ans = (ans + count) % mod
}
}
}
}
}
}
return ans
}