题目描述
给你一个 m x n
的网格。一个机器人从网格的左上角 (0, 0)
出发,目标是到达网格的右下角 (m - 1, n - 1)
。在任意时刻,机器人只能向右或向下移动。
网格中的每个单元格包含一个值 coins[i][j]
:
- 如果
coins[i][j] >= 0
,机器人可以获得该单元格的金币。
- 如果
coins[i][j] < 0
,机器人会遇到一个强盗,强盗会抢走该单元格数值的 绝对值 的金币。
机器人有一项特殊能力,可以在行程中 最多感化 2个单元格的强盗,从而防止这些单元格的金币被抢走。
注意:机器人的总金币数可以是负数。
返回机器人在路径上可以获得的 最大金币数 。
示例 1:
输入: coins = [[0,1,-1],[1,-2,3],[2,-3,4]]
输出: 8
解释:
一个获得最多金币的最优路径如下:
- 从
(0, 0)
出发,初始金币为 0
(总金币 = 0
)。
- 移动到
(0, 1)
,获得 1
枚金币(总金币 = 0 + 1 = 1
)。
- 移动到
(1, 1)
,遇到强盗抢走 2
枚金币。机器人在此处使用一次感化能力,避免被抢(总金币 = 1
)。
- 移动到
(1, 2)
,获得 3
枚金币(总金币 = 1 + 3 = 4
)。
- 移动到
(2, 2)
,获得 4
枚金币(总金币 = 4 + 4 = 8
)。
示例 2:
输入: coins = [[10,10,10],[10,10,10]]
输出: 40
解释:
一个获得最多金币的最优路径如下:
- 从
(0, 0)
出发,初始金币为 10
(总金币 = 10
)。
- 移动到
(0, 1)
,获得 10
枚金币(总金币 = 10 + 10 = 20
)。
- 移动到
(0, 2)
,再获得 10
枚金币(总金币 = 20 + 10 = 30
)。
- 移动到
(1, 2)
,获得 10
枚金币(总金币 = 30 + 10 = 40
)。
提示:
m == coins.length
n == coins[i].length
1 <= m, n <= 500
-1000 <= coins[i][j] <= 1000
解法
方法一:记忆化搜索
我们设计一个函数 $\textit{dfs}(i, j, k)$,表示机器人从 $(i, j)$ 出发,还剩下 $k$ 次感化机会时,能够获得的最大金币数。机器人只能向右或向下移动,因此 $\textit{dfs}(i, j, k)$ 的值只与 $\textit{dfs}(i + 1, j, k)$ 和 $\textit{dfs}(i, j + 1, k)$ 有关。
- 如果 $i \geq m$ 或 $j \geq n$,表示机器人走出了网格,此时返回一个极小值。
- 如果 $i = m - 1$ 且 $j = n - 1$,表示机器人到达了网格的右下角,此时如果 $k > 0$,则机器人可以选择感化当前位置的强盗,因此返回 $\max(0, \textit{coins}[i][j])$;如果 $k = 0$,则机器人不能感化当前位置的强盗,因此返回 $\textit{coins}[i][j]$。
- 如果 $\textit{coins}[i][j] < 0$,表示当前位置有强盗,此时如果 $k > 0$,则机器人可以选择感化当前位置的强盗,因此返回 $\textit{coins}[i][j] + \max(\textit{dfs}(i + 1, j, k), \textit{dfs}(i, j + 1, k))$;如果 $k = 0$,则机器人不能感化当前位置的强盗,因此返回 $\textit{coins}[i][j] + \max(\textit{dfs}(i + 1, j, k), \textit{dfs}(i, j + 1, k))$。
根据上述分析,我们可以编写出记忆化搜索的代码。
时间复杂度 $O(m \times n \times k)$,空间复杂度 $O(m \times n \times k)$。其中 $m$ 和 $n$ 分别是二维数组 $\textit{coins}$ 的行数和列数,而 $k$ 是感化机会的状态数,本题中 $k = 3$。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | class Solution:
def maximumAmount(self, coins: List[List[int]]) -> int:
@cache
def dfs(i: int, j: int, k: int) -> int:
if i >= m or j >= n:
return -inf
if i == m - 1 and j == n - 1:
return max(coins[i][j], 0) if k else coins[i][j]
ans = coins[i][j] + max(dfs(i + 1, j, k), dfs(i, j + 1, k))
if coins[i][j] < 0 and k:
ans = max(ans, dfs(i + 1, j, k - 1), dfs(i, j + 1, k - 1))
return ans
m, n = len(coins), len(coins[0])
return dfs(0, 0, 2)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | class Solution {
private Integer[][][] f;
private int[][] coins;
private int m;
private int n;
public int maximumAmount(int[][] coins) {
m = coins.length;
n = coins[0].length;
this.coins = coins;
f = new Integer[m][n][3];
return dfs(0, 0, 2);
}
private int dfs(int i, int j, int k) {
if (i >= m || j >= n) {
return Integer.MIN_VALUE / 2;
}
if (f[i][j][k] != null) {
return f[i][j][k];
}
if (i == m - 1 && j == n - 1) {
return k > 0 ? Math.max(0, coins[i][j]) : coins[i][j];
}
int ans = coins[i][j] + Math.max(dfs(i + 1, j, k), dfs(i, j + 1, k));
if (coins[i][j] < 0 && k > 0) {
ans = Math.max(ans, Math.max(dfs(i + 1, j, k - 1), dfs(i, j + 1, k - 1)));
}
return f[i][j][k] = ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | class Solution {
public:
int maximumAmount(vector<vector<int>>& coins) {
int m = coins.size(), n = coins[0].size();
vector<vector<vector<int>>> f(m, vector<vector<int>>(n, vector<int>(3, -1)));
auto dfs = [&](this auto&& dfs, int i, int j, int k) -> int {
if (i >= m || j >= n) {
return INT_MIN / 2;
}
if (f[i][j][k] != -1) {
return f[i][j][k];
}
if (i == m - 1 && j == n - 1) {
return k > 0 ? max(0, coins[i][j]) : coins[i][j];
}
int ans = coins[i][j] + max(dfs(i + 1, j, k), dfs(i, j + 1, k));
if (coins[i][j] < 0 && k > 0) {
ans = max({ans, dfs(i + 1, j, k - 1), dfs(i, j + 1, k - 1)});
}
return f[i][j][k] = ans;
};
return dfs(0, 0, 2);
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | func maximumAmount(coins [][]int) int {
m, n := len(coins), len(coins[0])
f := make([][][]int, m)
for i := range f {
f[i] = make([][]int, n)
for j := range f[i] {
f[i][j] = make([]int, 3)
for k := range f[i][j] {
f[i][j][k] = math.MinInt32
}
}
}
var dfs func(i, j, k int) int
dfs = func(i, j, k int) int {
if i >= m || j >= n {
return math.MinInt32 / 2
}
if f[i][j][k] != math.MinInt32 {
return f[i][j][k]
}
if i == m-1 && j == n-1 {
if k > 0 {
return max(0, coins[i][j])
}
return coins[i][j]
}
ans := coins[i][j] + max(dfs(i+1, j, k), dfs(i, j+1, k))
if coins[i][j] < 0 && k > 0 {
ans = max(ans, max(dfs(i+1, j, k-1), dfs(i, j+1, k-1)))
}
f[i][j][k] = ans
return ans
}
return dfs(0, 0, 2)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | function maximumAmount(coins: number[][]): number {
const [m, n] = [coins.length, coins[0].length];
const f = Array.from({ length: m }, () =>
Array.from({ length: n }, () => Array(3).fill(-Infinity)),
);
const dfs = (i: number, j: number, k: number): number => {
if (i >= m || j >= n) {
return -Infinity;
}
if (f[i][j][k] !== -Infinity) {
return f[i][j][k];
}
if (i === m - 1 && j === n - 1) {
return k > 0 ? Math.max(0, coins[i][j]) : coins[i][j];
}
let ans = coins[i][j] + Math.max(dfs(i + 1, j, k), dfs(i, j + 1, k));
if (coins[i][j] < 0 && k > 0) {
ans = Math.max(ans, dfs(i + 1, j, k - 1), dfs(i, j + 1, k - 1));
}
return (f[i][j][k] = ans);
};
return dfs(0, 0, 2);
}
|