跳转至

3418. 机器人可以获得的最大金币数

题目描述

给你一个 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

解释:

一个获得最多金币的最优路径如下:

  1. (0, 0) 出发,初始金币为 0(总金币 = 0)。
  2. 移动到 (0, 1),获得 1 枚金币(总金币 = 0 + 1 = 1)。
  3. 移动到 (1, 1),遇到强盗抢走 2 枚金币。机器人在此处使用一次感化能力,避免被抢(总金币 = 1)。
  4. 移动到 (1, 2),获得 3 枚金币(总金币 = 1 + 3 = 4)。
  5. 移动到 (2, 2),获得 4 枚金币(总金币 = 4 + 4 = 8)。

示例 2:

输入: coins = [[10,10,10],[10,10,10]]

输出: 40

解释:

一个获得最多金币的最优路径如下:

  1. (0, 0) 出发,初始金币为 10(总金币 = 10)。
  2. 移动到 (0, 1),获得 10 枚金币(总金币 = 10 + 10 = 20)。
  3. 移动到 (0, 2),再获得 10 枚金币(总金币 = 20 + 10 = 30)。
  4. 移动到 (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);
}

评论