2852. Sum of Remoteness of All Cells π
Problem Description
You have an n Γ n
matrix called grid
where each cell contains either:
- A positive integer value, or
-1
which represents a blocked cell
You can move between non-blocked cells that share an edge (up, down, left, right).
The remoteness R[i][j]
for each cell (i, j)
is defined as:
- If cell
(i, j)
is non-blocked:R[i][j]
equals the sum of all values in non-blocked cells that cannot reach cell(i, j)
through any path - If cell
(i, j)
is blocked (value is-1
):R[i][j] = 0
Your task is to calculate and return the sum of R[i][j]
for all cells in the grid.
Example understanding:
- If the grid has multiple disconnected groups of non-blocked cells (separated by blocked cells), then for any cell in one group, its remoteness would be the sum of all values in the other unreachable groups
- Cells within the same connected component can reach each other, so they don't contribute to each other's remoteness
The solution uses DFS to identify connected components. For each component with sum s
and size t
, every cell in that component contributes s
to the remoteness of all (cnt - t)
cells in other components, where cnt
is the total number of non-blocked cells in the grid.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: The grid represents a graph where non-blocked cells are nodes and edges exist between adjacent non-blocked cells that share an edge.
Is it a tree?
- No: The graph is not necessarily a tree. It can have multiple disconnected components (groups of connected non-blocked cells separated by blocked cells), and within each component, there could be cycles.
Is the problem related to directed acyclic graphs?
- No: The problem involves an undirected graph (you can move between adjacent cells in any direction).
Is the problem related to shortest paths?
- No: The problem is about finding connected components and calculating sums based on unreachable cells, not about finding shortest paths.
Does the problem involve connectivity?
- Yes: The core of the problem is identifying which cells are connected (can reach each other) and which cells are in separate components (cannot reach each other).
Disjoint Set Union
-
While DSU could work here, the solution uses DFS because we need to:
- Find all connected components
- Calculate the sum of values in each component
- Count the number of cells in each component
DFS naturally allows us to traverse each component while collecting these statistics.
Conclusion: The flowchart leads us to use DFS for this connectivity problem. DFS helps us identify connected components in the grid, calculate the sum of values in each component, and determine which cells cannot reach each other (are in different components) to compute the remoteness values.
Intuition
The key insight is recognizing that cells can only contribute to each other's remoteness if they cannot reach each other. This happens when cells belong to different connected components separated by blocked cells (-1
).
Think of the grid as islands of connected non-blocked cells separated by water (blocked cells). Each island is isolated from the others. The remoteness of a cell is essentially asking: "What is the sum of all cell values on other islands that I cannot reach?"
Let's break down the thought process:
-
Connected Components: First, we need to identify which cells belong together. Cells that can reach each other form a connected component. We can use DFS to explore each component starting from any unvisited non-blocked cell.
-
Component Statistics: For each connected component, we need two pieces of information:
- The sum
s
of all cell values in this component - The number of cells
t
in this component
- The sum
-
Calculating Remoteness: Here's the clever part - instead of calculating remoteness for each cell individually, we can think about it from a component perspective:
- If a component has sum
s
and containst
cells - There are
cnt - t
cells in other components (wherecnt
is the total number of non-blocked cells) - Each of those
cnt - t
cells will haves
added to their remoteness (since they cannot reach any cell in this component) - So this component contributes
s Γ (cnt - t)
to the total remoteness
- If a component has sum
-
Final Answer: By processing each connected component once and calculating its contribution to the total remoteness, we avoid redundant calculations. The sum of all these contributions gives us the final answer.
This approach is efficient because instead of checking reachability between every pair of cells, we leverage the fact that reachability is transitive within components and impossible between different components.
Learn more about Depth-First Search, Breadth-First Search and Union Find patterns.
Solution Approach
The implementation uses DFS to identify and process each connected component in the grid. Here's how the solution works:
1. Count Total Non-blocked Cells
cnt = sum(x > 0 for row in grid for x in row)
First, we count all non-blocked cells (positive values) in the entire grid. This gives us the total number of cells that can potentially contribute to remoteness calculations.
2. DFS Function for Component Exploration
def dfs(i: int, j: int) -> (int, int):
s, t = grid[i][j], 1
grid[i][j] = 0
for a, b in pairwise(dirs):
x, y = i + a, j + b
if 0 <= x < n and 0 <= y < n and grid[x][y] > 0:
s1, t1 = dfs(x, y)
s, t = s + s1, t + t1
return s, t
The DFS function explores a connected component starting from cell (i, j)
:
s
accumulates the sum of all cell values in the component (initialized with current cell's value)t
counts the number of cells in the component (initialized to 1)- We mark visited cells by setting them to 0 to avoid revisiting
- We explore all 4 directions using
dirs = (-1, 0, 1, 0, -1)
withpairwise
to get direction pairs - For each valid unvisited neighbor, we recursively call DFS and accumulate the results
3. Process Each Component
for i, row in enumerate(grid):
for j, x in enumerate(row):
if x > 0:
s, t = dfs(i, j)
ans += (cnt - t) * s
We iterate through the grid to find unvisited non-blocked cells:
- When we find one, it's the start of a new connected component
- We use DFS to get the component's sum
s
and sizet
- The contribution to total remoteness is
(cnt - t) * s
:(cnt - t)
is the number of cells in other components- Each of these cells has
s
added to their remoteness - This avoids double counting since we process each component only once
4. Key Optimization Instead of calculating remoteness for each cell individually, we calculate each component's contribution to the total remoteness in one go. This reduces the time complexity from O(nβ΄) (checking every pair of cells) to O(nΒ²) (visiting each cell once).
The algorithm cleverly uses the grid itself to track visited cells by setting them to 0, avoiding the need for a separate visited array.
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 to illustrate the solution approach.
Consider this 3Γ3 grid:
[ 2, -1, 3] [ 4, 5, -1] [-1, 6, 7]
Step 1: Count total non-blocked cells
- Non-blocked cells (positive values): 2, 3, 4, 5, 6, 7
- Total count
cnt = 6
Step 2: Identify connected components using DFS
Starting from top-left, we scan the grid:
-
Cell (0,0) has value 2 > 0, start DFS:
- Visit (0,0): value = 2
- Check neighbors: only (1,0) is reachable
- Visit (1,0): value = 4
- Check neighbors: only (1,1) is reachable
- Visit (1,1): value = 5
- No more reachable neighbors
- Component 1: sum = 2+4+5 = 11, size = 3 cells
-
Continue scanning, cell (0,2) has value 3 > 0, start DFS:
- Visit (0,2): value = 3
- No reachable neighbors (blocked by -1)
- Component 2: sum = 3, size = 1 cell
-
Continue scanning, cell (2,1) has value 6 > 0, start DFS:
- Visit (2,1): value = 6
- Check neighbors: only (2,2) is reachable
- Visit (2,2): value = 7
- No more reachable neighbors
- Component 3: sum = 6+7 = 13, size = 2 cells
Step 3: Calculate total remoteness
For each component, calculate its contribution to total remoteness:
-
Component 1 (sum=11, size=3):
- Cells in other components: cnt - size = 6 - 3 = 3
- Contribution: 11 Γ 3 = 33
- (Each of the 3 cells in other components has 11 added to their remoteness)
-
Component 2 (sum=3, size=1):
- Cells in other components: 6 - 1 = 5
- Contribution: 3 Γ 5 = 15
- (Each of the 5 cells in other components has 3 added to their remoteness)
-
Component 3 (sum=13, size=2):
- Cells in other components: 6 - 2 = 4
- Contribution: 13 Γ 4 = 52
- (Each of the 4 cells in other components has 13 added to their remoteness)
Total remoteness = 33 + 15 + 52 = 100
Verification (optional): Let's verify by calculating individual cell remoteness:
- Cells in Component 1 (2,4,5): Each has remoteness = 3 + 13 = 16
- Cell in Component 2 (3): Has remoteness = 11 + 13 = 24
- Cells in Component 3 (6,7): Each has remoteness = 11 + 3 = 14
- Total = 3Γ16 + 1Γ24 + 2Γ14 = 48 + 24 + 28 = 100 β
Solution Implementation
1class Solution:
2 def sumRemoteness(self, grid: List[List[int]]) -> int:
3 def dfs(row: int, col: int) -> tuple[int, int]:
4 """
5 Depth-first search to explore a connected component.
6
7 Args:
8 row: Current row index
9 col: Current column index
10
11 Returns:
12 tuple: (sum of values in component, count of cells in component)
13 """
14 # Get current cell value and mark it as visited by setting to 0
15 component_sum = grid[row][col]
16 component_count = 1
17 grid[row][col] = 0
18
19 # Explore all 4 adjacent cells (up, right, down, left)
20 for direction_idx in range(4):
21 next_row = row + directions[direction_idx]
22 next_col = col + directions[direction_idx + 1]
23
24 # Check if adjacent cell is within bounds and has positive value
25 if (0 <= next_row < grid_size and
26 0 <= next_col < grid_size and
27 grid[next_row][next_col] > 0):
28 # Recursively explore the adjacent cell
29 adjacent_sum, adjacent_count = dfs(next_row, next_col)
30 component_sum += adjacent_sum
31 component_count += adjacent_count
32
33 return component_sum, component_count
34
35 # Initialize grid size and direction vectors
36 grid_size = len(grid)
37 # Direction vectors for moving up, right, down, left
38 directions = [-1, 0, 1, 0, -1] # (row_delta, col_delta) pairs
39
40 # Count total number of positive cells in the grid
41 total_positive_cells = sum(cell > 0 for row in grid for cell in row)
42
43 # Calculate total remoteness
44 total_remoteness = 0
45
46 # Iterate through each cell in the grid
47 for row_idx, row in enumerate(grid):
48 for col_idx, cell_value in enumerate(row):
49 # If cell has positive value, it's the start of a new component
50 if cell_value > 0:
51 # Get sum and count of cells in this component
52 component_sum, component_size = dfs(row_idx, col_idx)
53
54 # Calculate remoteness for this component:
55 # (cells not in this component) * (sum of values in this component)
56 cells_outside_component = total_positive_cells - component_size
57 total_remoteness += cells_outside_component * component_sum
58
59 return total_remoteness
60
1class Solution {
2 private int gridSize;
3 private int[][] grid;
4 // Direction vectors for moving up, right, down, left (4 directions)
5 private final int[] directions = {-1, 0, 1, 0, -1};
6
7 public long sumRemoteness(int[][] grid) {
8 this.gridSize = grid.length;
9 this.grid = grid;
10
11 // Count total number of cells with positive values
12 int totalPositiveCells = 0;
13 for (int[] row : grid) {
14 for (int value : row) {
15 if (value > 0) {
16 totalPositiveCells++;
17 }
18 }
19 }
20
21 long totalRemoteness = 0;
22
23 // Process each connected component
24 for (int row = 0; row < gridSize; row++) {
25 for (int col = 0; col < gridSize; col++) {
26 if (grid[row][col] > 0) {
27 // Find the sum and count of cells in this connected component
28 long[] componentInfo = exploreComponent(row, col);
29 long componentSum = componentInfo[0];
30 long componentCellCount = componentInfo[1];
31
32 // Calculate remoteness: cells not in this component * sum of this component
33 long cellsOutsideComponent = totalPositiveCells - componentCellCount;
34 totalRemoteness += cellsOutsideComponent * componentSum;
35 }
36 }
37 }
38
39 return totalRemoteness;
40 }
41
42 /**
43 * Performs DFS to explore a connected component starting from (row, col)
44 * @param row starting row position
45 * @param col starting column position
46 * @return array where [0] is sum of values in component, [1] is count of cells in component
47 */
48 private long[] exploreComponent(int row, int col) {
49 long[] result = new long[2];
50 result[0] = grid[row][col]; // Sum of values in this component
51 result[1] = 1; // Count of cells in this component
52
53 // Mark current cell as visited by setting it to 0
54 grid[row][col] = 0;
55
56 // Explore all 4 adjacent cells
57 for (int dirIndex = 0; dirIndex < 4; dirIndex++) {
58 int nextRow = row + directions[dirIndex];
59 int nextCol = col + directions[dirIndex + 1];
60
61 // Check if next position is valid and has a positive value
62 if (nextRow >= 0 && nextRow < gridSize &&
63 nextCol >= 0 && nextCol < gridSize &&
64 grid[nextRow][nextCol] > 0) {
65
66 // Recursively explore the adjacent cell
67 long[] adjacentResult = exploreComponent(nextRow, nextCol);
68 result[0] += adjacentResult[0]; // Add sum from adjacent component
69 result[1] += adjacentResult[1]; // Add count from adjacent component
70 }
71 }
72
73 return result;
74 }
75}
76
1class Solution {
2public:
3 long long sumRemoteness(vector<vector<int>>& grid) {
4 // Type alias for pair of (sum, count)
5 using PairLongInt = pair<long long, int>;
6
7 int gridSize = grid.size();
8
9 // Count total number of cells with positive values
10 int totalPositiveCells = 0;
11 for (const auto& row : grid) {
12 for (int value : row) {
13 if (value > 0) {
14 totalPositiveCells++;
15 }
16 }
17 }
18
19 // Direction vectors for moving up, right, down, left
20 int directions[5] = {-1, 0, 1, 0, -1};
21
22 // DFS function to explore connected components
23 // Returns pair of (sum of values in component, number of cells in component)
24 function<PairLongInt(int, int)> depthFirstSearch = [&](int row, int col) {
25 // Initialize sum with current cell value and count as 1
26 long long componentSum = grid[row][col];
27 int componentSize = 1;
28
29 // Mark current cell as visited by setting to 0
30 grid[row][col] = 0;
31
32 // Explore all 4 adjacent cells
33 for (int dir = 0; dir < 4; ++dir) {
34 int nextRow = row + directions[dir];
35 int nextCol = col + directions[dir + 1];
36
37 // Check if next cell is within bounds and has positive value
38 if (nextRow >= 0 && nextRow < gridSize &&
39 nextCol >= 0 && nextCol < gridSize &&
40 grid[nextRow][nextCol] > 0) {
41
42 // Recursively explore the adjacent cell
43 auto [adjacentSum, adjacentCount] = depthFirstSearch(nextRow, nextCol);
44 componentSum += adjacentSum;
45 componentSize += adjacentCount;
46 }
47 }
48
49 return PairLongInt(componentSum, componentSize);
50 };
51
52 // Calculate total remoteness
53 long long totalRemoteness = 0;
54
55 // Iterate through all cells in the grid
56 for (int row = 0; row < gridSize; ++row) {
57 for (int col = 0; col < gridSize; ++col) {
58 // Process each unvisited positive cell (start of a new component)
59 if (grid[row][col] > 0) {
60 // Get sum and size of current connected component
61 auto [componentSum, componentSize] = depthFirstSearch(row, col);
62
63 // Calculate remoteness: cells not in this component * sum of this component
64 totalRemoteness += (totalPositiveCells - componentSize) * componentSum;
65 }
66 }
67 }
68
69 return totalRemoteness;
70 }
71};
72
1/**
2 * Calculates the sum of remoteness for all connected components in a grid
3 * @param grid - 2D array where positive values represent nodes
4 * @returns The total sum of remoteness across all components
5 */
6function sumRemoteness(grid: number[][]): number {
7 const gridSize: number = grid.length;
8
9 // Count total number of positive cells in the grid
10 let totalPositiveCells: number = 0;
11 for (const row of grid) {
12 for (const cellValue of row) {
13 if (cellValue > 0) {
14 totalPositiveCells++;
15 }
16 }
17 }
18
19 // Direction vectors for exploring 4 adjacent cells (up, right, down, left)
20 const directions: number[] = [-1, 0, 1, 0, -1];
21
22 /**
23 * Depth-first search to explore a connected component
24 * @param row - Current row index
25 * @param col - Current column index
26 * @returns Tuple of [sum of values in component, count of cells in component]
27 */
28 const depthFirstSearch = (row: number, col: number): [number, number] => {
29 // Initialize sum with current cell value and count as 1
30 let componentSum: number = grid[row][col];
31 let componentCount: number = 1;
32
33 // Mark current cell as visited by setting to 0
34 grid[row][col] = 0;
35
36 // Explore all 4 adjacent directions
37 for (let dirIndex = 0; dirIndex < 4; dirIndex++) {
38 const nextRow: number = row + directions[dirIndex];
39 const nextCol: number = col + directions[dirIndex + 1];
40
41 // Check if next cell is within bounds and has positive value
42 if (nextRow >= 0 && nextRow < gridSize &&
43 nextCol >= 0 && nextCol < gridSize &&
44 grid[nextRow][nextCol] > 0) {
45
46 // Recursively explore the adjacent cell
47 const [adjacentSum, adjacentCount] = depthFirstSearch(nextRow, nextCol);
48 componentSum += adjacentSum;
49 componentCount += adjacentCount;
50 }
51 }
52
53 return [componentSum, componentCount];
54 };
55
56 let totalRemoteness: number = 0;
57
58 // Process each connected component in the grid
59 for (let rowIndex = 0; rowIndex < gridSize; rowIndex++) {
60 for (let colIndex = 0; colIndex < gridSize; colIndex++) {
61 if (grid[rowIndex][colIndex] > 0) {
62 // Get sum and count for current connected component
63 const [componentSum, componentCount] = depthFirstSearch(rowIndex, colIndex);
64
65 // Calculate remoteness contribution:
66 // (cells outside component) * (sum of values in component)
67 totalRemoteness += (totalPositiveCells - componentCount) * componentSum;
68 }
69 }
70 }
71
72 return totalRemoteness;
73}
74
Time and Space Complexity
Time Complexity: O(nΒ²)
The algorithm iterates through each cell in the n Γ n
grid exactly once in the main loop. For each cell that contains a positive value, it initiates a DFS traversal. The DFS function visits each cell in a connected component exactly once (marking visited cells by setting them to 0). Since each cell is visited at most once throughout the entire execution (either skipped if already 0 or visited once during DFS), the total number of operations is proportional to the number of cells in the grid, which is nΒ²
.
Space Complexity: O(nΒ²)
The space complexity consists of two main components:
- Recursion stack space: In the worst case, the DFS could traverse all cells in the grid if they form a single connected component. This would create a recursion depth of
O(nΒ²)
in the call stack. - Input grid modification: The algorithm modifies the input grid in-place by setting visited cells to 0, but this doesn't add extra space beyond the input.
Therefore, the dominant factor is the recursion stack, giving us a space complexity of O(nΒ²)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Modifying the Original Grid
The most significant pitfall in this solution is that it modifies the input grid by setting visited cells to 0. This destructive approach can cause issues if:
- The grid needs to be preserved for other operations
- The function is called multiple times on the same grid
- The problem explicitly requires maintaining the original input
Solution: Use a separate visited tracking mechanism instead of modifying the grid:
def sumRemoteness(self, grid: List[List[int]]) -> int:
grid_size = len(grid)
visited = [[False] * grid_size for _ in range(grid_size)]
def dfs(row: int, col: int) -> tuple[int, int]:
visited[row][col] = True
component_sum = grid[row][col]
component_count = 1
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
next_row, next_col = row + dr, col + dc
if (0 <= next_row < grid_size and
0 <= next_col < grid_size and
not visited[next_row][next_col] and
grid[next_row][next_col] > 0):
adjacent_sum, adjacent_count = dfs(next_row, next_col)
component_sum += adjacent_sum
component_count += adjacent_count
return component_sum, component_count
2. Confusing Direction Array Usage
The direction array [-1, 0, 1, 0, -1]
with index-based access can be confusing and error-prone. Developers might incorrectly access indices or misunderstand the pattern.
Solution: Use explicit direction pairs for clarity:
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] # right, down, left, up for dr, dc in directions: next_row = row + dr next_col = col + dc
3. Integer Overflow for Large Grids
For very large grids with high values, the multiplication (cnt - t) * s
could potentially overflow in languages with fixed integer sizes. While Python handles arbitrary precision integers, this could be an issue when porting to other languages.
Solution: Be aware of the maximum possible values and use appropriate data types:
- For a grid of size nΓn with maximum value V, the worst case is approximately nΒ² Γ nΒ² Γ V
- In other languages, consider using long/int64 types
- Add validation for extremely large inputs if necessary
4. Incorrect Handling of Edge Cases
The code doesn't explicitly handle edge cases like:
- Empty grid (n = 0)
- Grid with all blocked cells (-1 values)
- Grid with no blocked cells
Solution: Add explicit edge case handling:
def sumRemoteness(self, grid: List[List[int]]) -> int:
if not grid or not grid[0]:
return 0
grid_size = len(grid)
total_positive_cells = sum(cell > 0 for row in grid for cell in row)
# If no positive cells or all cells are connected, remoteness is 0
if total_positive_cells == 0:
return 0
5. Misunderstanding the Problem Definition
Developers might incorrectly calculate remoteness by:
- Including cells from the same component
- Double-counting contributions
- Misinterpreting what "cannot reach" means
Solution: Add clear documentation and consider breaking down the calculation:
# For each component, calculate its contribution to total remoteness: # - Component with sum S and size T # - There are (total_cells - T) cells in other components # - Each of these cells has S added to their remoteness # - Total contribution: (total_cells - T) * S
What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?
Recommended Readings
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Union Find Disjoint Set Union Data Structure Introduction Prerequisite Depth First Search Review problems dfs_intro Once we have a strong grasp of recursion and Depth First Search we can now introduce Disjoint Set Union DSU This data structure is motivated by the following problem Suppose we have sets of elements
Want a Structured Path to Master System Design Too? Donβt Miss This!