Facebook Pixel

3619. Count Islands With Total Value Divisible by K

Problem Description

You have a 2D matrix grid with m rows and n columns, along with a positive integer k. The matrix contains integers where positive values represent land.

An island is defined as a group of positive integers (land cells) that are connected horizontally or vertically (4-directionally connected). This means two land cells belong to the same island if you can travel from one to the other by moving only up, down, left, or right through other land cells.

The total value of an island is calculated by adding up all the values of the cells that belong to that island.

Your task is to count how many islands have a total value that is divisible by k. In other words, find the number of islands where the sum of all cell values in that island, when divided by k, gives a remainder of 0.

For example, if an island consists of cells with values [3, 4, 2], the total value would be 9. If k = 3, this island would be counted since 9 % 3 = 0.

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 2D grid can be viewed as a graph where each cell is a node, and edges exist between adjacent cells (horizontally and vertically). The problem involves finding connected components (islands) in this implicit graph structure.

Is it a tree?

  • No: The grid structure is not a tree. It's a general graph that can have multiple connected components (islands), and within each component, there might be cycles (you can traverse from one cell back to itself through adjacent cells).

Is the problem related to directed acyclic graphs?

  • No: The connections between cells are undirected (if cell A is adjacent to cell B, then B is also adjacent to A), and the graph can contain cycles.

Is the problem related to shortest paths?

  • No: We're not looking for shortest paths between nodes. Instead, we need to find all connected components (islands) and calculate their total values.

Does the problem involve connectivity?

  • Yes: The core of the problem is about connectivity - we need to identify groups of connected cells (islands) and process each connected component separately.

Does the problem have small constraints?

  • Yes: While not explicitly stated in the problem, grid problems typically have manageable constraints that allow for DFS/BFS traversal without causing stack overflow or timeout issues.

DFS/backtracking?

  • Yes: DFS is perfect for exploring all cells in a connected component (island). Starting from any unvisited land cell, we can use DFS to visit all connected land cells, sum their values, and mark them as visited.

Conclusion: The flowchart leads us to use DFS (Depth-First Search) for solving this island connectivity problem. DFS allows us to explore each island completely, calculate its total value, and check if it's divisible by k.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we look at this problem, we need to find groups of connected positive numbers (islands) and check if their sum is divisible by k. The key insight is that this is fundamentally a connected component problem in disguise.

Think of the grid as a map where positive numbers represent land and zeros (or negative numbers) represent water. We need to explore each piece of land and determine which island it belongs to. Once we identify an island, we calculate its total value.

The natural approach is to traverse the grid cell by cell. When we encounter a positive number that hasn't been visited yet, we've found a new island. From this starting point, we need to explore the entire island to calculate its total value. This exploration process is perfectly suited for DFS because:

  1. We need to visit every cell in the island exactly once - DFS ensures we don't miss any connected cells and don't count cells twice.

  2. We need to accumulate values as we explore - During the DFS traversal, we can sum up the values of all cells we visit, giving us the island's total value.

  3. We need to mark visited cells - To avoid counting the same island multiple times, we mark cells as visited. A clever trick is to set visited cells to 0, effectively "sinking" the island after we've processed it.

The algorithm flow becomes clear: scan through the grid, and whenever we find an unvisited land cell (positive value), launch a DFS from that cell. The DFS will explore the entire island, return the sum of all its cells, and mark all those cells as visited. If this sum is divisible by k, we increment our counter.

This approach elegantly handles all islands in the grid with a time complexity of O(m Γ— n) since each cell is visited at most once, making it an efficient solution for this connectivity problem.

Learn more about Depth-First Search, Breadth-First Search and Union Find patterns.

Solution Approach

The implementation uses DFS to explore each island and calculate its total value. Let's walk through the solution step by step:

Core DFS Function

We define a helper function dfs(i, j) that performs depth-first search starting from position (i, j). This function serves two purposes:

  1. Explores all cells belonging to the same island
  2. Returns the total sum of values in that island

The DFS works as follows:

  • First, we capture the current cell's value in variable s (this will be our running sum)
  • Mark the current cell as visited by setting grid[i][j] = 0 (this prevents revisiting)
  • Explore all four adjacent directions (up, down, left, right)
  • For each valid adjacent cell that has a positive value, recursively call DFS and add its contribution to our sum
  • Return the total sum of the island

Direction Navigation

The code uses a clever pattern for exploring four directions:

dirs = (-1, 0, 1, 0, -1)

Combined with pairwise(dirs), this generates the pairs: (-1, 0), (0, 1), (1, 0), (0, -1), which represent movements up, right, down, and left respectively.

Main Algorithm

The main function follows this logic:

  1. Initialize dimensions m and n of the grid
  2. Set up the direction array for navigation
  3. Initialize answer counter ans = 0
  4. Iterate through every cell (i, j) in the grid
  5. When we find a cell with a positive value (unvisited land):
    • Call dfs(i, j) to get the island's total value
    • Check if this total is divisible by k using modulo operation
    • If total % k == 0, increment the answer counter
  6. Return the final count

Key Implementation Details

  • In-place modification: The grid is modified during traversal (cells set to 0), which serves as our visited tracking mechanism. This saves space compared to maintaining a separate visited array.
  • Boundary checking: The condition 0 <= x < m and 0 <= y < n ensures we stay within grid boundaries.
  • Island identification: The check grid[i][j] in the main loop ensures we only start DFS from unvisited land cells.
  • Divisibility check: The expression dfs(i, j) % k == 0 efficiently checks if the island's total value is divisible by k.

This approach visits each cell exactly once, giving us a time complexity of O(m Γ— n) and space complexity of O(min(m, n)) for the recursion stack in the worst case.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to understand how the solution works.

Consider this grid with k = 5:

grid = [[1, 3, 0, 2],
        [2, 0, 0, 3],
        [0, 4, 1, 0]]

Step 1: Initial Setup

  • We have a 3Γ—4 grid (m=3, n=4)
  • k = 5 (we're looking for islands whose sum is divisible by 5)
  • Answer counter starts at 0

Step 2: Scan the Grid

Starting from position (0,0):

  • We find value 1 (positive), so we've discovered an island!
  • Launch DFS from (0,0)

Step 3: First Island DFS Exploration

DFS from (0,0) with value 1:

  • Mark current cell as visited: grid[0][0] = 0
  • Check all 4 directions:
    • Up: Out of bounds
    • Right: (0,1) has value 3 β†’ recursively DFS
    • Down: (1,0) has value 2 β†’ recursively DFS
    • Left: Out of bounds

DFS from (0,1) with value 3:

  • Mark as visited: grid[0][1] = 0
  • Check neighbors: all are 0 or out of bounds
  • Return 3

DFS from (1,0) with value 2:

  • Mark as visited: grid[1][0] = 0
  • Check neighbors: all are 0 or out of bounds
  • Return 2

Back to original DFS: Total sum = 1 + 3 + 2 = 6

  • Check: 6 % 5 = 1 (not divisible by 5)
  • Answer remains 0

Grid after first island:

[[0, 0, 0, 2],
 [0, 0, 0, 3],
 [0, 4, 1, 0]]

Step 4: Continue Scanning

Continue from (0,2): value is 0, skip

At position (0,3):

  • Find value 2, new island discovered!
  • Launch DFS from (0,3)

Step 5: Second Island DFS

DFS from (0,3) with value 2:

  • Mark as visited: grid[0][3] = 0
  • Check down: (1,3) has value 3 β†’ recursively DFS

DFS from (1,3) with value 3:

  • Mark as visited: grid[1][3] = 0
  • Return 3

Total sum = 2 + 3 = 5

  • Check: 5 % 5 = 0 (divisible by 5!)
  • Increment answer to 1

Grid after second island:

[[0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 4, 1, 0]]

Step 6: Find Last Island

Continue scanning until position (2,1):

  • Find value 4, new island!
  • Launch DFS from (2,1)

DFS from (2,1) with value 4:

  • Mark as visited: grid[2][1] = 0
  • Check right: (2,2) has value 1 β†’ recursively DFS

DFS from (2,2) with value 1:

  • Mark as visited: grid[2][2] = 0
  • Return 1

Total sum = 4 + 1 = 5

  • Check: 5 % 5 = 0 (divisible by 5!)
  • Increment answer to 2

Final Result

The algorithm found 3 islands total:

  • Island 1: {1, 3, 2} with sum 6 (not divisible by 5)
  • Island 2: {2, 3} with sum 5 (divisible by 5) βœ“
  • Island 3: {4, 1} with sum 5 (divisible by 5) βœ“

Return answer = 2

This walkthrough demonstrates how DFS systematically explores each island, calculates its sum, and checks divisibility, while the in-place modification prevents revisiting cells.

Solution Implementation

1class Solution:
2    def countIslands(self, grid: List[List[int]], k: int) -> int:
3        """
4        Count the number of islands whose sum of values is divisible by k.
5      
6        Args:
7            grid: 2D grid where non-zero values represent land
8            k: The divisor to check island sums against
9          
10        Returns:
11            Number of islands whose sum is divisible by k
12        """
13        def dfs(row: int, col: int) -> int:
14            """
15            Depth-first search to explore an island and calculate its sum.
16            Marks visited cells by setting them to 0.
17          
18            Args:
19                row: Current row index
20                col: Current column index
21              
22            Returns:
23                Sum of all values in the connected island
24            """
25            # Store the current cell's value
26            island_sum = grid[row][col]
27          
28            # Mark current cell as visited by setting to 0
29            grid[row][col] = 0
30          
31            # Explore all 4 adjacent directions (up, right, down, left)
32            for i in range(4):
33                next_row = row + directions[i]
34                next_col = col + directions[i + 1]
35              
36                # Check if the adjacent cell is within bounds and is land (non-zero)
37                if (0 <= next_row < rows and 
38                    0 <= next_col < cols and 
39                    grid[next_row][next_col] != 0):
40                    # Recursively explore and add to island sum
41                    island_sum += dfs(next_row, next_col)
42          
43            return island_sum
44      
45        # Get grid dimensions
46        rows, cols = len(grid), len(grid[0])
47      
48        # Direction vectors for moving up, right, down, left
49        # Pairs: (-1,0), (0,1), (1,0), (0,-1)
50        directions = [-1, 0, 1, 0, -1]
51      
52        # Counter for islands divisible by k
53        island_count = 0
54      
55        # Traverse the entire grid
56        for i in range(rows):
57            for j in range(cols):
58                # If we find unvisited land, explore the island
59                if grid[i][j] != 0:
60                    # Get the sum of the entire island
61                    total_sum = dfs(i, j)
62                  
63                    # Check if island sum is divisible by k
64                    if total_sum % k == 0:
65                        island_count += 1
66      
67        return island_count
68
1class Solution {
2    // Grid dimensions
3    private int rows;
4    private int cols;
5    private int[][] grid;
6  
7    // Direction vectors for exploring 4 adjacent cells (up, right, down, left)
8    // Using pairs: (-1,0), (0,1), (1,0), (0,-1)
9    private final int[] directions = {-1, 0, 1, 0, -1};
10
11    /**
12     * Counts the number of islands whose sum of values is divisible by k
13     * @param grid 2D array where positive values represent land
14     * @param k divisor to check island sums against
15     * @return number of islands with sum divisible by k
16     */
17    public int countIslands(int[][] grid, int k) {
18        // Initialize grid dimensions
19        this.rows = grid.length;
20        this.cols = grid[0].length;
21        this.grid = grid;
22      
23        int islandCount = 0;
24      
25        // Traverse entire grid to find islands
26        for (int row = 0; row < rows; row++) {
27            for (int col = 0; col < cols; col++) {
28                // If current cell is land (positive value), explore the island
29                if (grid[row][col] > 0) {
30                    long islandSum = dfs(row, col);
31                    // Check if island sum is divisible by k
32                    if (islandSum % k == 0) {
33                        islandCount++;
34                    }
35                }
36            }
37        }
38      
39        return islandCount;
40    }
41
42    /**
43     * Performs depth-first search to calculate sum of all connected land cells
44     * Marks visited cells by setting them to 0
45     * @param row current row position
46     * @param col current column position
47     * @return sum of all connected land cells forming an island
48     */
49    private long dfs(int row, int col) {
50        // Add current cell value to sum
51        long sum = grid[row][col];
52      
53        // Mark current cell as visited by setting to 0
54        grid[row][col] = 0;
55      
56        // Explore all 4 adjacent directions
57        for (int dir = 0; dir < 4; dir++) {
58            int nextRow = row + directions[dir];
59            int nextCol = col + directions[dir + 1];
60          
61            // Check if next position is valid and contains unvisited land
62            if (nextRow >= 0 && nextRow < rows && 
63                nextCol >= 0 && nextCol < cols && 
64                grid[nextRow][nextCol] > 0) {
65                // Recursively explore and add to sum
66                sum += dfs(nextRow, nextCol);
67            }
68        }
69      
70        return sum;
71    }
72}
73
1class Solution {
2public:
3    int countIslands(vector<vector<int>>& grid, int k) {
4        int rows = grid.size();
5        int cols = grid[0].size();
6      
7        // Direction vectors for moving up, right, down, left
8        // Using pairs: (-1,0), (0,1), (1,0), (0,-1)
9        vector<int> directions = {-1, 0, 1, 0, -1};
10
11        // Recursive lambda function to perform DFS and calculate island sum
12        // Uses explicit this parameter (C++23 feature) for recursive lambda
13        auto depthFirstSearch = [&](this auto&& depthFirstSearch, int row, int col) -> long long {
14            // Store current cell value as the starting sum
15            long long islandSum = grid[row][col];
16          
17            // Mark current cell as visited by setting it to 0
18            grid[row][col] = 0;
19          
20            // Explore all 4 adjacent directions
21            for (int dir = 0; dir < 4; ++dir) {
22                int nextRow = row + directions[dir];
23                int nextCol = col + directions[dir + 1];
24              
25                // Check if the next cell is within bounds and contains a non-zero value
26                if (nextRow >= 0 && nextRow < rows && 
27                    nextCol >= 0 && nextCol < cols && 
28                    grid[nextRow][nextCol] != 0) {
29                    // Recursively add the sum of connected cells
30                    islandSum += depthFirstSearch(nextRow, nextCol);
31                }
32            }
33          
34            return islandSum;
35        };
36
37        int islandCount = 0;
38      
39        // Iterate through each cell in the grid
40        for (int i = 0; i < rows; ++i) {
41            for (int j = 0; j < cols; ++j) {
42                // If cell is non-zero (part of an unvisited island)
43                if (grid[i][j] != 0) {
44                    // Calculate the sum of the entire island
45                    long long currentIslandSum = depthFirstSearch(i, j);
46                  
47                    // If the island sum is divisible by k, increment counter
48                    if (currentIslandSum % k == 0) {
49                        ++islandCount;
50                    }
51                }
52            }
53        }
54      
55        return islandCount;
56    }
57};
58
1/**
2 * Counts the number of islands in a grid where the sum of values is divisible by k
3 * @param grid - 2D array representing the map where positive values form islands
4 * @param k - The divisor to check if island sum is divisible by
5 * @returns Number of islands whose sum is divisible by k
6 */
7function countIslands(grid: number[][], k: number): number {
8    const rows: number = grid.length;
9    const cols: number = grid[0].length;
10  
11    // Direction vectors for exploring 4 adjacent cells (up, right, down, left)
12    // Using a single array: dirs[i] and dirs[i+1] form (dx, dy) pairs
13    const directions: number[] = [-1, 0, 1, 0, -1];
14  
15    /**
16     * Performs depth-first search to explore an island and calculate its sum
17     * @param row - Current row index
18     * @param col - Current column index
19     * @returns Sum of all values in the connected island
20     */
21    const dfs = (row: number, col: number): number => {
22        // Store current cell value as part of island sum
23        let islandSum: number = grid[row][col];
24      
25        // Mark current cell as visited by setting to 0
26        grid[row][col] = 0;
27      
28        // Explore all 4 adjacent directions
29        for (let dir: number = 0; dir < 4; dir++) {
30            const nextRow: number = row + directions[dir];
31            const nextCol: number = col + directions[dir + 1];
32          
33            // Check if next cell is within bounds and is part of an island (value > 0)
34            if (nextRow >= 0 && nextRow < rows && 
35                nextCol >= 0 && nextCol < cols && 
36                grid[nextRow][nextCol] > 0) {
37                // Recursively explore and add to island sum
38                islandSum += dfs(nextRow, nextCol);
39            }
40        }
41      
42        return islandSum;
43    };
44  
45    let islandCount: number = 0;
46  
47    // Iterate through all cells in the grid
48    for (let i: number = 0; i < rows; i++) {
49        for (let j: number = 0; j < cols; j++) {
50            // If cell is part of an unvisited island (value > 0)
51            if (grid[i][j] > 0) {
52                // Calculate island sum and check if divisible by k
53                const sum: number = dfs(i, j);
54                if (sum % k === 0) {
55                    islandCount++;
56                }
57            }
58        }
59    }
60  
61    return islandCount;
62}
63

Time and Space Complexity

Time Complexity: O(m Γ— n) where m is the number of rows and n is the number of columns in the grid.

The algorithm visits each cell in the grid exactly once. The outer loops iterate through all m Γ— n cells. When a cell containing a non-zero value is found, the DFS function is called, which marks that cell and all connected cells as visited by setting them to 0. Since each cell is visited and processed only once (either skipped if already 0 or processed by DFS and set to 0), the total time complexity is O(m Γ— n).

Space Complexity: O(m Γ— n) where m is the number of rows and n is the number of columns in the grid.

The space complexity is dominated by the recursion stack used in the DFS traversal. In the worst case, when all cells in the grid form a single connected component (all cells have value 1), the DFS could potentially go as deep as m Γ— n levels. This happens when the connected component forms a long path through all cells. Additionally, the algorithm modifies the input grid in-place, but this doesn't add to the space complexity since the grid already exists. The dirs tuple and other variables use O(1) extra space.

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 in-place by setting visited cells to 0. This destructive approach has several issues:

  • Data Loss: The original grid values are permanently lost after the function executes
  • Reusability: The grid cannot be used for subsequent operations or analyses
  • Side Effects: Unexpected behavior if the caller expects the grid to remain unchanged

Solution: Create a separate visited tracking mechanism:

def countIslands(self, grid: List[List[int]], k: int) -> int:
    rows, cols = len(grid), len(grid[0])
    visited = [[False] * cols for _ in range(rows)]
  
    def dfs(row: int, col: int) -> int:
        visited[row][col] = True
        island_sum = grid[row][col]
      
        for dr, dc in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
            next_row, next_col = row + dr, col + dc
            if (0 <= next_row < rows and 
                0 <= next_col < cols and 
                not visited[next_row][next_col] and 
                grid[next_row][next_col] > 0):  # Check for positive values
                island_sum += dfs(next_row, next_col)
      
        return island_sum
  
    island_count = 0
    for i in range(rows):
        for j in range(cols):
            if not visited[i][j] and grid[i][j] > 0:
                if dfs(i, j) % k == 0:
                    island_count += 1
  
    return island_count

2. Confusion Between Zero and Negative Values

The current implementation treats zero as "water" or "non-land", but the problem states that positive integers represent land. This creates ambiguity:

  • What if the grid contains negative values?
  • Should zero be treated as water or as visited land?

Solution: Be explicit about handling different value types:

# Clear condition for what constitutes land
if grid[i][j] > 0:  # Only positive values are land
    # Process island

3. Stack Overflow Risk with Large Islands

Using recursive DFS can cause stack overflow for very large islands due to Python's recursion limit (default ~1000).

Solution: Use iterative DFS with an explicit stack:

def dfs_iterative(start_row: int, start_col: int) -> int:
    stack = [(start_row, start_col)]
    island_sum = 0
    visited[start_row][start_col] = True
  
    while stack:
        row, col = stack.pop()
        island_sum += grid[row][col]
      
        for dr, dc in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
            next_row, next_col = row + dr, col + dc
            if (0 <= next_row < rows and 
                0 <= next_col < cols and 
                not visited[next_row][next_col] and 
                grid[next_row][next_col] > 0):
                visited[next_row][next_col] = True
                stack.append((next_row, next_col))
  
    return island_sum

4. Integer Overflow for Large Island Sums

While Python handles arbitrarily large integers, in other languages or when dealing with extremely large grids, the sum might overflow.

Solution: Consider using modular arithmetic if only divisibility matters:

def dfs(row: int, col: int) -> int:
    island_sum = grid[row][col] % k  # Keep sum modulo k
    visited[row][col] = True
  
    for dr, dc in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
        next_row, next_col = row + dr, col + dc
        if (0 <= next_row < rows and 
            0 <= next_col < cols and 
            not visited[next_row][next_col] and 
            grid[next_row][next_col] > 0):
            island_sum = (island_sum + dfs(next_row, next_col)) % k
  
    return island_sum

Then check if the final result equals 0 for divisibility.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following array represent a max heap?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More