【LetMeFly】3418.机器人可以获得的最大金币数:动态规划 力扣题目链接:https://leetcode.cn/problems/maximum-amount-of-money-robot-can-earn/
给你一个 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)。
提示:
n == coins.length
m == coins[i].length
1 <= m, n <= 500
-1000 <= coins[i][j] <= 1000
解题方法:动态规划 令$dp[th][i][j]$代表到达$(i, j)$时感化强盗最多$th$次的最大收益。
如果不感化:$dp[th][i][j]=coins[i][j] + \max(dp[th][i-1][j], dp[th][i][j-1])$(上左最大的那个 + 本格子)
如果感化:$dp[th][i][j] = \max(dp[th-1][i-1][j], dp[th-1][i][j-1])$ (上左$th-1$次感化中最大的那个)
注意判断下边界条件$th$、$i$、$j$大于或等于$0$就好。
时间复杂度$O(nm)$
空间复杂度$O(nm)$
AC代码 C++ 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 const int INF = 1e9 ;class Solution {public : int maximumAmount (vector<vector<int >>& coins) { int n = coins.size (), m = coins[0 ].size (); array<vector<vector<int >>, 3> dp; dp.fill (vector<vector<int >>(n, vector <int >(m))); dp[0 ][0 ][0 ] = coins[0 ][0 ]; dp[1 ][0 ][0 ] = dp[2 ][0 ][0 ] = max (coins[0 ][0 ], 0 ); for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { if (!i && !j) { continue ; } for (int th = 0 ; th < 3 ; th++) { dp[th][i][j] = coins[i][j] + max (i ? dp[th][i - 1 ][j] : -INF, j ? dp[th][i][j - 1 ] : -INF); } for (int th = 1 ; th < 3 ; th++) { dp[th][i][j] = max (dp[th][i][j], max (i ? dp[th - 1 ][i - 1 ][j] : -INF, j ? dp[th - 1 ][i][j - 1 ] : -INF)); } } } return dp[2 ][n - 1 ][m - 1 ]; } };
Python 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ''' LastEditTime: 2026-04-04 14:23:48 ''' from typing import List from math import infclass Solution : def maximumAmount (self, coins: List [List [int ]] ) -> int : n, m = len (coins), len (coins[0 ]) dp = [[[-inf for _ in range (m)] for _ in range (n)] for _ in range (3 )] dp[0 ][0 ][0 ] = coins[0 ][0 ] dp[1 ][0 ][0 ] = dp[2 ][0 ][0 ] = max (coins[0 ][0 ], 0 ) for i in range (n): for j in range (m): if not i and not j: continue for th in range (3 ): dp[th][i][j] = max (dp[th][i - 1 ][j] if i else -inf, dp[th][i][j - 1 ] if j else -inf) + coins[i][j] for th in range (1 , 3 ): dp[th][i][j] = max (dp[th][i][j], dp[th - 1 ][i - 1 ][j] if i else -inf, dp[th - 1 ][i][j - 1 ] if j else -inf) return dp[2 ][n - 1 ][m - 1 ]
空间优化 不难发现新的一行dp只需要利用上一行的dp结果,所以我们的dp数组没必要开$n$行$m$列,只需要开$1$行$m$列就好。
AC代码 C++ 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 const int INF = 1e9 ;class Solution {public : int maximumAmount (vector<vector<int >>& coins) { int n = coins.size (), m = coins[0 ].size (); array<vector<int >, 3> dp; dp.fill (vector <int >(m)); for (int i = 0 ; i < n; i++) { array<vector<int >, 3> nextDP; nextDP.fill (vector <int >(m)); for (int j = 0 ; j < m; j++) { if (!i && !j) { nextDP[0 ][0 ] = coins[0 ][0 ]; nextDP[1 ][0 ] = nextDP[2 ][0 ] = max (coins[0 ][0 ], 0 ); continue ; } for (int th = 0 ; th < 3 ; th++) { nextDP[th][j] = coins[i][j] + max (i ? dp[th][j] : -INF, j ? nextDP[th][j - 1 ] : -INF); } for (int th = 1 ; th < 3 ; th++) { nextDP[th][j] = max (nextDP[th][j], max (i ? dp[th - 1 ][j] : -INF, j ? nextDP[th - 1 ][j - 1 ] : -INF)); } } dp = move (nextDP); } return dp[2 ][m - 1 ]; } };
同步发文于CSDN 和我的个人博客 ,原创不易,转载经作者同意后请附上原文链接 哦~
千篇源码题解已开源