2025-07-23:机器人可以获得的最大金币数。用go语言,你有一个大小为 m 行 n 列的网格。机器人从左上角位置(0,0)开始,目标是走到右下角位置(m-1,n-1)。机器人每步只能往右边或往下边移动。

网格中每个格子有一个整数 coins[i][j]:

  • 如果值 >= 0,机器人会获得对应数量的金币;
  • 如果值 < 0,表示此格子有强盗,机器人会被抢走相应数量金币(即该值的绝对值)。

不过,机器人有一次特殊能力,可以在整个行程中最多“感化”2个强盗,也就是说最多有2个负值格子不会造成金币损失。

最终,求机器人在满足移动规则和特殊能力限制下,所能获得的金币最大值。
需要注意,最终金币总数可以是负数。

m == coins.length。

n == coins[i].length。

1 <= m, n <= 500。

-1000 <= coins[i][j] <= 1000。

输入: 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)。

题目来自力扣3418。


一、动态规划状态定义

  1. 状态数组f[i][j][k]
  • 代表机器人走到第 i 行第 j 列格子时,已经“感化”了 k(0~2)个强盗,获得的最大金币数。
  1. 状态转移方向
  • 由于机器人只能向右或者向下移动,所以到达 (i,j) 必须来自 (i-1,j)(i,j-1)
  1. 状态转移方程
    对于当前格子金币数 val = coins[i][j]
  • 如果 val >= 0,只需把之前状态金币加上 val,感化次数不变。
  • 如果 val < 0,可以选择感化强盗(即扣金币次数加1,但不扣金币数)或者不感化(扣金币数,但感化次数不增加)。
  • 每种可能选择都更新对应 f[i][j][k] 的最大金币数。

二、代码处理(参考代码中的思路)

注意原代码中的 f 是对 coins 按行维度的一维压缩方案,且用长度为 n+1 的数组保存当前列位置最大金币数,且第三维为“感化次数”。

具体操作:

  1. 初始化状态
  • 把状态全部初始化为极小值(math.MinInt / 2),表示不可达或初始不可能状态。
  • f[1] = [3]int{0,0,0},初始位置金币为0,以方便下面累加。
  1. 逐行处理
  • 遍历每行 row,对于每个格子(列 j),更新 f[j+1] 的3个状态:
  • f[j+1][0]:当前格子不感化负金币,累加金币或扣除金币。
  • f[j+1][1]:当前格子感化第1个负金币,更新状态。
  • f[j+1][2]:当前格子感化第2个负金币,更新状态。
  • 对更新用 max 函数来决定不同路径中获得的最大金币数。
  1. 返回结果
  • 最终返回的结果是 f[n][2],即走到最后单元格感化2个强盗的最大金币。

三、具体过程举例(简化说明)

假设2x3的例子 coins=[[10,10,10],[10,10,10]]

  • 初始化状态,同一行中每个格子存储3个状态(0感化,1感化,2感化),每个状态存储最大金币。
  • 第一行遍历,累加金币,因为均为正数,感化数不会增加,最大金币为 10->20->30
  • 第二行,继续累加金币,路径可能从左侧或上一行的相同列过来,因为都为正数,最大金币为 30+10=40
  • 最终机器人获得40金币。

四、时间复杂度

  • 每个格子 (m*n) 都需要计算3个感化状态。
  • 每个状态更新时常数复杂度操作。
  • 时间复杂度 = O(m * n * 3) ≈ O(m * n)

五、空间复杂度

  • 代码中用了一维的状态数组 f,长度为 n+1,每个位置存储3个状态整数。
  • 空间复杂度 = O(n * 3) = O(n)。
  • 如果没使用压缩,就是 O(m * n * 3)

总结

内容

说明

状态含义

f[i][j][k] 表示在 (i,j) 感化 k 个强盗的最大金币数

状态转移

来源于上方或左方格子,更新感化数和金币数

感化次数

最高2次,3个状态对应0、1、2次感化

时间复杂度

O(m * n)

空间复杂度

O(n) (代码中一维空间压缩)

通过此方法,合理地利用状态将“感化次数”因素融入路径规划,能在线性时间和较少空间内完成搜索,适合大规模输入。

Go完整代码如下:

package main

import (
	"fmt"
	"math"
)

func maximumAmount(coins [][]int) int {
	n := len(coins[0])
	f := make([][3]int, n+1)
	for j := range f {
		f[j] = [3]int{math.MinInt / 2, math.MinInt / 2, math.MinInt / 2}
	}
	f[1] = [3]int{}
	for _, row := range coins {
		for j, x := range row {
			f[j+1][2] = max(f[j][2]+x, f[j+1][2]+x, f[j][1], f[j+1][1])
			f[j+1][1] = max(f[j][1]+x, f[j+1][1]+x, f[j][0], f[j+1][0])
			f[j+1][0] = max(f[j][0], f[j+1][0]) + x
		}
	}
	return f[n][2]
}

func main() {
	coins := [][]int{{10, 10, 10}, {10, 10, 10}}
	result := maximumAmount(coins)
	fmt.Println(result)
}

2025-07-23:机器人可以获得的最大金币数。用go语言,你有一个大小为 m 行 n 列的网格。机器人从左上角位置(0,0)开始,目标是走到右下角位置(m-1,n-1)。机器人每步只能往右边或往下_开发语言

Python完整代码如下:

# -*-coding:utf-8-*-

def maximumAmount(coins):
    if not coins or not coins[0]:
        return 0
    n = len(coins[0])
    # 初始化dp数组,f[j]表示到第j列(0-indexed)的三种状态
    f = [[-10**18, -10**18, -10**18] for _ in range(n+1)]
    f[1] = [0, 0, 0]  # 起始点初始化
    
    for row in coins:
        for j, x in enumerate(row):
            # 状态转移方程
            s2 = max(
                f[j][2] + x,    # 从左边的状态2向右移动
                f[j+1][2] + x,  # 从上方的状态2向下移动(连续第三次向下,但按状态2处理)
                f[j][1],        # 从左边的状态1转移(不连续向下)
                f[j+1][1]       # 从上方的状态1转移(不连续向下)
            )
            s1 = max(
                f[j][1] + x,    # 从左边的状态1向右移动
                f[j+1][1] + x,  # 从上方的状态1向下移动(第一次向下)
                f[j][0],        # 从左边的状态0转移
                f[j+1][0]       # 从上方的状态0转移
            )
            s0 = max(f[j][0], f[j+1][0]) + x  # 状态0(最后一步向右)
            
            # 更新下一列的状态
            f[j+1] = [s0, s1, s2]
    
    return f[n][2]

# 测试示例
if __name__ == "__main__":
    coins = [[10, 10, 10], [10, 10, 10]]
    result = maximumAmount(coins)
    print(result)  # 输出结果

2025-07-23:机器人可以获得的最大金币数。用go语言,你有一个大小为 m 行 n 列的网格。机器人从左上角位置(0,0)开始,目标是走到右下角位置(m-1,n-1)。机器人每步只能往右边或往下_开发语言_02