Maximum Amount of Money Robot Can Earn

Problem LinkSolution Link

Intuition #

The problem is a dynamic programming problem with a small twist which is that we can neutralize robbers that rob the robot if the cell is negative at most 2 times.

This will introduce an additional constraint to cell position which is the number of neutralize left to use.

R is len(coins) and C is len(coins[0])-1. nLeft is the number of neutralizations left.

Starting from (0, 0) and going to (R-1, C-1), we at each step consider two cases:

We need to consider base cases:

Can an overflow happen if we return -\infin?

Positiondp(y+1, x)dp(y, x+1)max(...) result
Middle cellreal valuereal valuereal value
Last rowMinIntreal valuereal value
Last columnreal valueMinIntreal value
Bottom-rightcaught by base case first

The table shows when we return -\infin and the result of the max function used to get the max path value.

Notice that max never has math.MinInt, math.MinInt to result in an integer overflow if we add to it a negative number.

If we do not caught the last cell (bottom right position) an overflow will happen because the table will become:

Positiondp(y+1, x)dp(y, x+1)max(...) result
Middle cellreal valuereal valuereal value
Last rowMinIntreal valuereal value
Last columnreal valueMinIntreal value
Bottom-rightMinIntMinIntOVERFLOW

Approach #

Complexity #

Code #

 1import "math"
 2
 3func maximumAmount(coins [][]int) int {
 4	R, C := len(coins), len(coins[0])
 5
 6	memo := make([][][3]int, R)
 7	for y := range R {
 8		memo[y] = make([][3]int, C)
 9		for x := range C {
10			for k := range 3 {
11				memo[y][x][k] = math.MinInt
12			}
13		}
14	}
15
16	var dp func(y, x, nLeft int) int
17	dp = func(y, x, nLeft int) int {
18		if y > len(coins)-1 || x > len(coins[0])-1 {
19			return math.MinInt // Does an overflow might happen? No.
20		}
21
22		// Handling the last cell explicitly to
23		// prevent overflow, because if we
24		// are at the last cell the dp function
25		// will do current+dp(math.MinInt, math.MinInt),
26		// which will result in an integer overflow.
27		if y == len(coins)-1 && x == len(coins[0])-1 {
28			if nLeft > 0 {
29				return max(0, coins[y][x])
30			}
31
32			return coins[y][x]
33		}
34
35		if memo[y][x][nLeft] != math.MinInt {
36			return memo[y][x][nLeft]
37		}
38
39		current := coins[y][x]
40
41		// Having current+ outside of the max function
42		// will prevent integer overflow
43		// for cells out of bounds.
44		v := current + max(dp(y+1, x, nLeft), dp(y, x+1, nLeft))
45
46		if nLeft > 0 && current < 0 {
47			v = max(v, dp(y+1, x, nLeft-1))
48			v = max(v, dp(y, x+1, nLeft-1))
49		}
50
51		memo[y][x][nLeft] = v
52		return v
53	}
54
55	return dp(0, 0, 2)
56}