2017. Grid Game
Problem Description
You have a 2x n grid where each cell contains points. Two robots will traverse this grid in sequence, both starting at position (0, 0) and ending at position (1, n-1).
The movement rules are:
- Each robot can only move right (from (r, c) to (r, c+1)) or down (from (r, c) to (r+1, c))
- Since the grid has only 2 rows, each robot must move down exactly once at some position
The game proceeds as follows:
- The first robot goes from (0, 0) to (1, n-1), collecting all points on its path
- All cells visited by the first robot have their points set to 0
- The second robot then goes from (0, 0) to (1, n-1), collecting points from the remaining cells
The key insight is that the first robot's path will always look like: moving right along row 0 for some distance, moving down to row 1, then continuing right to the end. This creates a division where the second robot can only collect points from either:
- The remaining cells in row 0 (to the right of where robot 1 went down)
- The remaining cells in row 1 (to the left of where robot 1 went down)
The first robot wants to minimize the second robot's score, while the second robot wants to maximize its score. Both robots play optimally.
The solution uses prefix sums to efficiently calculate the maximum points the second robot can collect for each possible turning point of the first robot. Variable s1
tracks the sum of remaining points in row 0 (initially all points, then decreased as we move right), and s2
tracks the sum of points in row 1 up to the current position (initially 0, then increased as we move right). For each position j
where robot 1 might turn down, the second robot's optimal score is max(s1, s2)
. The answer is the minimum of all these maximum values.
Intuition
Since the grid has only 2 rows, the first robot's path has a very specific structure - it must move right along the top row for some number of steps, then move down exactly once, and continue right along the bottom row until reaching the end. This creates a "turning point" that divides the grid into two regions.
Once the first robot chooses where to turn down (say at column j
), it collects all points from cells (0,0)
to (0,j)
in the top row, and cells (1,j)
to (1,n-1)
in the bottom row. These cells become 0.
The second robot, playing optimally, now has two choices:
- Stay on the top row as long as possible to collect points from cells
(0,j+1)
to(0,n-1)
- Drop down immediately to collect points from cells
(1,0)
to(1,j-1)
The second robot will choose whichever option gives more points. So for any turning point j
chosen by the first robot, the second robot scores max(top_remaining, bottom_remaining)
.
The first robot, knowing this, wants to choose the turning point j
that minimizes this maximum. This leads to a minimax problem: min over all j of max(top_remaining, bottom_remaining)
.
To implement this efficiently, we can maintain running sums:
s1
starts as the sum of all top row values, and we subtractgrid[0][j]
as we consider each turning point moving rights2
starts at 0 and accumulatesgrid[1][j]
as we move right
For each position j
, s1
represents points available in the top row after position j
, and s2
represents points available in the bottom row before position j
. The second robot's best score at this configuration is max(s1, s2)
, and we track the minimum across all positions.
Learn more about Prefix Sum patterns.
Solution Approach
The solution uses a prefix sum technique to efficiently calculate the optimal result for the minimax problem.
Initialization:
ans = inf
: Initialize the answer to infinity since we're looking for the minimum values1 = sum(grid[0])
: Calculate the total sum of all points in the first rows2 = 0
: Initialize the prefix sum of the second row to 0
Main Algorithm:
We iterate through each column j
from 0 to n-1, considering it as the potential turning point where the first robot moves down:
-
Update suffix sum for row 0:
s1 -= v
wherev = grid[0][j]
- After this subtraction,
s1
represents the sum of points in row 0 from columnj+1
ton-1
- These are the points the second robot could collect if it stays on row 0 longer
- After this subtraction,
-
Calculate the second robot's maximum score:
max(s1, s2)
- The second robot will choose the better of two options:
- Collect remaining points in row 0: value is
s1
- Drop down early and collect points in row 1 before column
j
: value iss2
- Collect remaining points in row 0: value is
- The second robot will choose the better of two options:
-
Update the answer:
ans = min(ans, max(s1, s2))
- The first robot wants to minimize the second robot's maximum possible score
-
Update prefix sum for row 1:
s2 += grid[1][j]
- Add the current cell's value to the prefix sum
- This prepares
s2
for the next iteration where it will represent points from column 0 toj
Why this works:
The key insight is that we process the turning points from left to right. At each position j
:
s1
has already excluded points that the first robot collected from row 0 (columns 0 to j)s2
contains exactly the points available in row 1 before column j
This allows us to compute the second robot's optimal score for each possible first robot strategy in O(1) time per position, resulting in an overall O(n) time complexity with O(1) extra space.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with a 2×3 grid:
grid = [[2, 5, 4], [1, 5, 1]]
Initial Setup:
ans = infinity
s1 = sum(grid[0]) = 2 + 5 + 4 = 11
(sum of entire top row)s2 = 0
(no bottom row cells counted yet)
Iteration 1: j = 0 (robot 1 turns down at column 0)
s1 = 11 - 2 = 9
(remaining top row: columns 1-2 have sum 5+4=9)- Second robot's best score:
max(s1, s2) = max(9, 0) = 9
- Option 1: Stay top and collect columns 1-2 of row 0 → score = 9
- Option 2: Drop immediately (no cells available in row 1) → score = 0
- Update
ans = min(infinity, 9) = 9
- Update
s2 = 0 + 1 = 1
(add grid[1][0] for next iteration)
Iteration 2: j = 1 (robot 1 turns down at column 1)
s1 = 9 - 5 = 4
(remaining top row: column 2 has value 4)- Second robot's best score:
max(s1, s2) = max(4, 1) = 4
- Option 1: Stay top and collect column 2 of row 0 → score = 4
- Option 2: Drop early and collect column 0 of row 1 → score = 1
- Update
ans = min(9, 4) = 4
- Update
s2 = 1 + 5 = 6
(add grid[1][1] for next iteration)
Iteration 3: j = 2 (robot 1 turns down at column 2)
s1 = 4 - 4 = 0
(no remaining cells in top row)- Second robot's best score:
max(s1, s2) = max(0, 6) = 6
- Option 1: No cells left in row 0 → score = 0
- Option 2: Drop early and collect columns 0-1 of row 1 → score = 6
- Update
ans = min(4, 6) = 4
- Update
s2 = 6 + 1 = 7
(not used further)
Result: The answer is 4. This happens when robot 1 moves right once (collecting 2, 5), then moves down at column 1, and continues right (collecting 5, 1). Robot 2 can then optimally collect the cell at position (0,2) with value 4.
Solution Implementation
1class Solution:
2 def gridGame(self, grid: List[List[int]]) -> int:
3 # Initialize the minimum possible score for second robot to infinity
4 min_second_robot_score = float('inf')
5
6 # top_remaining: sum of all cells in top row (what's left for robot 2 if it goes top)
7 # bottom_collected: sum of cells in bottom row up to current position (what robot 2 gets if it goes bottom)
8 top_remaining = sum(grid[0])
9 bottom_collected = 0
10
11 # Try each possible column where robot 1 switches from top to bottom row
12 for column_index, cell_value in enumerate(grid[0]):
13 # Remove current cell from top row (robot 1 took it)
14 top_remaining -= cell_value
15
16 # Robot 2 will take the maximum of:
17 # - Remaining cells in top row (if it stays on top)
18 # - Collected cells in bottom row (if it goes to bottom before robot 1's switch point)
19 second_robot_score = max(top_remaining, bottom_collected)
20
21 # Update minimum score robot 2 can achieve
22 min_second_robot_score = min(min_second_robot_score, second_robot_score)
23
24 # Add current column's bottom cell to accumulated bottom sum
25 bottom_collected += grid[1][column_index]
26
27 return min_second_robot_score
28
1class Solution {
2 public long gridGame(int[][] grid) {
3 // Initialize the minimum possible score for the second robot
4 long minSecondRobotScore = Long.MAX_VALUE;
5
6 // Calculate the total sum of the first row (top row)
7 long topRowRemainingSum = 0;
8 for (int value : grid[0]) {
9 topRowRemainingSum += value;
10 }
11
12 // Get the number of columns in the grid
13 int columns = grid[0].length;
14
15 // Track the sum of the second row (bottom row) up to current position
16 long bottomRowPrefixSum = 0;
17
18 // Try each possible column where the first robot goes down
19 for (int columnIndex = 0; columnIndex < columns; columnIndex++) {
20 // Remove current cell from top row sum (first robot takes this path)
21 topRowRemainingSum -= grid[0][columnIndex];
22
23 // The second robot will take the maximum of:
24 // 1. Remaining cells in the top row (after columnIndex)
25 // 2. Cells in the bottom row before columnIndex
26 long secondRobotScore = Math.max(topRowRemainingSum, bottomRowPrefixSum);
27
28 // Update the minimum possible score for the second robot
29 minSecondRobotScore = Math.min(minSecondRobotScore, secondRobotScore);
30
31 // Add current bottom cell to the prefix sum for next iteration
32 bottomRowPrefixSum += grid[1][columnIndex];
33 }
34
35 return minSecondRobotScore;
36 }
37}
38
1class Solution {
2public:
3 long long gridGame(vector<vector<int>>& grid) {
4 // Initialize the minimum possible score for the second robot
5 long long minSecondRobotScore = LLONG_MAX;
6
7 // Get the number of columns in the grid
8 int numColumns = grid[0].size();
9
10 // Calculate the total sum of the first row (top row)
11 long long topRowRemainingSum = 0;
12 for (int value : grid[0]) {
13 topRowRemainingSum += value;
14 }
15
16 // Bottom row prefix sum (accumulated as we iterate)
17 long long bottomRowPrefixSum = 0;
18
19 // Try each column as the turning point where first robot goes down
20 for (int col = 0; col < numColumns; ++col) {
21 // Remove current column from top row sum (first robot takes this path)
22 topRowRemainingSum -= grid[0][col];
23
24 // Second robot will take the maximum of:
25 // 1. Remaining top row sum (if it stays on top after first robot goes down)
26 // 2. Bottom row prefix sum (if it goes down immediately)
27 long long secondRobotScore = max(topRowRemainingSum, bottomRowPrefixSum);
28
29 // We want to minimize what the second robot can get
30 minSecondRobotScore = min(minSecondRobotScore, secondRobotScore);
31
32 // Add current bottom cell to prefix sum for next iteration
33 bottomRowPrefixSum += grid[1][col];
34 }
35
36 return minSecondRobotScore;
37 }
38};
39
1/**
2 * Calculates the minimum possible score for the second robot in a grid game.
3 * The first robot collects points while moving from top-left to bottom-right,
4 * and the second robot follows collecting remaining points.
5 *
6 * @param grid - A 2x n grid where grid[i][j] represents points at position (i, j)
7 * @returns The minimum possible maximum score the second robot can achieve
8 */
9function gridGame(grid: number[][]): number {
10 // Initialize the minimum answer with the maximum safe integer value
11 let minimumSecondRobotScore: number = Number.MAX_SAFE_INTEGER;
12
13 // Calculate the total sum of the first row (top path)
14 // This represents points available to the right of where first robot goes down
15 let topRowRemainingSum: number = grid[0].reduce((accumulator: number, current: number) => accumulator + current, 0);
16
17 // Initialize the sum of the second row (bottom path)
18 // This represents points available to the left of where first robot goes down
19 let bottomRowPrefixSum: number = 0;
20
21 // Iterate through each column to find the optimal point for the first robot to go down
22 for (let columnIndex: number = 0; columnIndex < grid[0].length; columnIndex++) {
23 // Remove current cell from top row remaining sum (first robot takes this path)
24 topRowRemainingSum -= grid[0][columnIndex];
25
26 // The second robot will take the maximum of:
27 // - Points remaining in the top row (to the right)
28 // - Points accumulated in the bottom row (to the left)
29 // We want to minimize this maximum value
30 minimumSecondRobotScore = Math.min(minimumSecondRobotScore, Math.max(topRowRemainingSum, bottomRowPrefixSum));
31
32 // Add current bottom cell to the prefix sum for next iteration
33 bottomRowPrefixSum += grid[1][columnIndex];
34 }
35
36 return minimumSecondRobotScore;
37}
38
Time and Space Complexity
The time complexity is O(n)
, where n
is the number of columns in the grid. The algorithm iterates through each column exactly once using a single for loop that goes through grid[0]
. In each iteration, it performs constant time operations: subtracting v
from s1
, computing the minimum and maximum, and adding grid[1][j]
to s2
.
The space complexity is O(1)
. The algorithm only uses a fixed number of variables (ans
, s1
, s2
, j
, v
) regardless of the input size. No additional data structures that scale with the input are created.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Initial Values for Prefix/Suffix Sums
A common mistake is initializing s1
or s2
incorrectly, such as including or excluding cells that shouldn't be counted at the start.
Incorrect approach:
# Wrong: Including the first cell of row 0 in both robot paths
s1 = sum(grid[0][1:]) # Missing grid[0][0]
s2 = grid[1][0] # Including a cell that might not be collected
Correct approach:
s1 = sum(grid[0]) # Initially all of row 0
s2 = 0 # Initially none of row 1
2. Wrong Order of Operations in the Loop
The order of updating s1
, calculating the maximum, updating the answer, and updating s2
is crucial. Changing this order leads to incorrect results.
Incorrect approach:
for j in range(n):
s2 += grid[1][j] # Wrong: Adding before calculating max
s1 -= grid[0][j]
ans = min(ans, max(s1, s2))
Correct approach:
for j in range(n):
s1 -= grid[0][j] # First: remove from top
ans = min(ans, max(s1, s2)) # Second: calculate minimax
s2 += grid[1][j] # Last: add to bottom for next iteration
3. Misunderstanding the Game Theory Aspect
Some might think both robots cooperate or that the first robot tries to maximize its own score. The key is understanding that robot 1 plays adversarially to minimize robot 2's score.
Incorrect interpretation:
# Wrong: Trying to maximize robot 1's score
max_robot1_score = 0
for j in range(n):
robot1_score = sum(grid[0][:j+1]) + sum(grid[1][j:])
max_robot1_score = max(max_robot1_score, robot1_score)
Correct interpretation: The solution correctly implements the minimax strategy where robot 1 minimizes the maximum score robot 2 can achieve.
4. Edge Case: Single Column Grid (n=1)
When the grid has only one column, both robots must take the same path, and robot 2 gets nothing. Some implementations might not handle this correctly.
Potential issue:
# If n=1, the loop runs once with j=0 # After s1 -= grid[0][0], s1 becomes 0 # s2 is still 0 # max(s1, s2) = 0, which is correct
The provided solution handles this correctly, but it's worth verifying edge cases explicitly.
5. Off-by-One Errors in Understanding Cell Collections
A common confusion is about which cells each robot collects. When robot 1 turns down at column j:
- Robot 1 collects:
grid[0][0...j]
andgrid[1][j...n-1]
- Robot 2 can collect either:
grid[0][j+1...n-1]
ORgrid[1][0...j-1]
Incorrect mental model:
Thinking robot 2 can collect grid[1][0...j]
(including column j in row 1), which would be wrong since robot 1 already took grid[1][j]
.
Solution:
The code correctly handles this by updating s2
after calculating the maximum, ensuring grid[1][j]
is only available for the next iteration's calculation.
Which of the following array represent a max heap?
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!