Maximum Number of Fish in a Grid

Updated on 17 June, 2025
Maximum Number of Fish in a Grid header image

Problem Statement

In this challenge, you are given a two-dimensional grid, representing a rectangular fishing area where each cell of the grid either indicates land or contains a certain number of fish. The cells are zero-indexed. Based on the value of each cell (r, c):

  • A value of 0 indicates a land cell where fishing is not possible.
  • Any positive integer value indicates a water cell that contains that number of fish.

The objective for the fisher is to start fishing from any water cell and maximize the catch by potentially moving to other adjacent water cells. From any given cell grid[r][c], the fisher can perform the following operations repeatedly:

  1. Catch all the fish available at the current cell grid[r][c].
  2. Move to an adjacent cell that is directly to the left, right, above, or below the current cell, provided it is also a water cell.

The task is to compute the maximum number of fish that can be caught starting from the optimal cell under these conditions. If there are no water cells, the result should be 0.

Adjacency is defined conventionally, so for a cell (r, c) in the grid, the adjacent cells are considered to be (r, c+1), (r, c-1), (r+1, c), and (r-1, c), assuming these cells fall within grid bounds.

Examples

Example 1

Input:

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

Output:

7

Explanation:

The fisher can start at cell `(1,3)` and collect 3 fish, then move to cell `(2,3)` and collect 4 fish.

Example 2

Input:

grid = [[1,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,1]]

Output:

1

Explanation:

The fisher can start at cells (0,0) or (3,3) and collect a single fish.

Constraints

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 10
  • 0 <= grid[i][j] <= 10

Approach and Intuition

To tackle the problem of maximizing the fish caught:

  1. Identify all the water cells in the grid.
  2. For each water cell identified, simulate the process of starting at that cell and moving to adjacent water cells to determine the total number of fish that can be caught. This involves:
    • Tracking the amount of fish at the starting water cell.
    • Utilizing a strategy such as Depth-First Search (DFS) or Breadth-First Search (BFS) to move from one water cell to adjacent ones, accumulating the count of fish.
  3. As you apply the above strategy to each water cell, keep track of the maximum fish caught starting from any water cell.
  4. After evaluating all possible starting points, the maximum value obtained will be the answer to the problem.

In examining the operations, key aspects are to navigate only through water cells and sum up the fish as the fisher moves, ensuring that no cell is visited more than once to prevent recounting fish. Given the problem constraints, with grid sizes constrained to 10x10, a direct simulation with BFS or DFS should perform efficiently within acceptable time limits.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
private:
    // Function to determine the root with path compression
    int findRoot(vector<int>& ancestors, int index) {
        if (ancestors[index] == index) {
            return index;
        }
        return ancestors[index] = findRoot(ancestors, ancestors[index]);  // Compress path
    }
    
    // Function to connect two components
    void mergeComponents(vector<int>& ancestors, vector<int>& sizes,
                         vector<int>& fishes, int index1,
                         int index2) {
        int rootA = findRoot(ancestors, index1);
        int rootB = findRoot(ancestors, index2);
    
        if (rootA != rootB) {  // Only merge if different roots
            // Merge by size
            if (sizes[rootA] < sizes[rootB]) {
                swap(rootA, rootB);
            }
            ancestors[rootB] = rootA;
            sizes[rootA] += sizes[rootB];
            fishes[rootA] += fishes[rootB];
        }
    }
    
public:
    int computeMaxFish(vector<vector<int>>& matrix) {
        int rows = matrix.size(), cols = matrix[0].size();
        int n = rows * cols;
    
        vector<int> ancestors(n), sizes(n, 1), totalFishes(n);
    
        iota(ancestors.begin(), ancestors.end(), 0);
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                int index = r * cols + c;
                totalFishes[index] = matrix[r][c];
            }
        }
    
        vector<int> dRow{0, 0, 1, -1}, dCol{1, -1, 0, 0};
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (matrix[r][c] > 0) {
                    int index = r * cols + c;
                    for (int d = 0; d < 4; d++) {
                        int nr = r + dRow[d];
                        int nc = c + dCol[d];
                        if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && matrix[nr][nc] > 0) {
                            int nIndex = nr * cols + nc;
                            mergeComponents(ancestors, sizes, totalFishes, index, nIndex);
                        }
                    }
                }
            }
        }
    
        int maxFish = 0;
        for (int i = 0; i < n; i++) {
            if (findRoot(ancestors, i) == i) {  // Root cell of component
                maxFish = max(maxFish, totalFishes[i]);
            }
        }
        return maxFish;
    }
};

The C++ solution presented addresses the problem of calculating the maximum number of fish that can accumulate in a grid. This grid allows for fish from different cells to merge into a larger group if the cells are adjacent to each other.

The key components of this solution are:

  • Union-Find Data Structure: Implements two primary operations using path compression and union by size:

    • findRoot: Determines the root of the component containing a particular element, optimizing the path to the root by compression.
    • mergeComponents: Joins two components, ensuring the smaller component (in terms of size) gets merged into the larger one, and accumulates the number of fish accordingly.
  • Main Logic - computeMaxFish Function:

    1. Initializes arrays to track the root, size, and total fish count of each cell/component.
    2. Iterates over each cell in the grid, initially treating each cell as its own component with fish equal to the cell's initial value.
    3. Looks at 4 potential directions from each cell (left, right, up, down) to find adjacent cells that contain fish and can be merged.
    4. At the end of the process, traverses through the array to find the root components and identifies the maximum fish count from these components.

***Usage of Vectors: * **ancestors: Maintains the root of each component. * sizes: Keeps track of the size of each component. * totalFishes: Records the aggregated number of fish per component.

This solution efficiently groups cells and calculates the maximum number of fish using a union-find algorithm with path compression and careful merging strategies, allowing it to operate efficiently even on larger grids. The structure ensures each component's root is easily accessible and that merging takes place in a way that optimizes the size and total fish computation.

java
class Solution {
    // Define function to get root of the set
    private int findSetRoot(int[] setRoots, int index) {
        if (setRoots[index] == index) {
            return index;
        }
        return setRoots[index] = findSetRoot(setRoots, setRoots[index]); // Compression heuristic
    }
    
    // Define function to merge two sets
    private void mergeSets(
        int[] setRoots,
        int[] setSize,
        int[] fishCount,
        int indexOne,
        int indexTwo
    ) {
        int rootOne = findSetRoot(setRoots, indexOne);
        int rootTwo = findSetRoot(setRoots, indexTwo);
    
        if (rootOne != rootTwo) {
            // Union by size to maintain nearly flat structure
            if (setSize[rootOne] < setSize[rootTwo]) {
                int temp = rootOne;
                rootOne = rootTwo;
                rootTwo = temp;
            }
            setRoots[rootTwo] = rootOne;
            setSize[rootOne] += setSize[rootTwo]; // Increase the size of the root set
            fishCount[rootOne] += fishCount[rootTwo]; // Sum fish counts
        }
    }
    
    public int calculateMaxFish(int[][] fishGrid) {
        int numRows = fishGrid.length, numCols = fishGrid[0].length;
        int totalIndices = numRows * numCols;
    
        // Initialization of sets for Union-Find
        int[] setRoots = new int[totalIndices];
        int[] setSize = new int[totalIndices];
        int[] fishCount = new int[totalIndices];
    
        for (int idx = 0; idx < totalIndices; idx++) {
            setRoots[idx] = idx; // Each index is initially its own root
            setSize[idx] = 1; // Initial size of each set component
            fishCount[idx] = fishGrid[idx / numCols][idx % numCols]; // Fish counts as per grid
        }
    
        // Vectors to navigate neighbors (4 directions)
        int[] rowOffset = {0, 0, 1, -1}, colOffset = {1, -1, 0, 0};
    
        // Process and unite the adjacent sets
        for (int row = 0; row < numRows; row++) {
            for (int col = 0; col < numCols; col++) {
                int currentIndex = row * numCols + col;
                if (fishGrid[row][col] > 0) {
                    for (int dir = 0; dir < 4; dir++) {
                        int newRow = row + rowOffset[dir];
                        int newCol = col + colOffset[dir];
                        if (
                            newRow >= 0 &&
                            newRow < numRows &&
                            newCol >= 0 &&
                            newCol < numCols &&
                            fishGrid[newRow][newCol] > 0
                        ) {
                            int neighborIndex = newRow * numCols + newCol;
                            mergeSets(setRoots, setSize, fishCount, currentIndex, neighborIndex);
                        }
                    }
                }
            }
        }
    
        // Calculate max fish count of root sets
        int maxFish = 0;
        for (int idx = 0; idx < totalIndices; idx++) {
            if (findSetRoot(setRoots, idx) == idx) { // Check if the index is the root
                maxFish = Math.max(maxFish, fishCount[idx]);
            }
        }
        return maxFish;
    }
}

The given Java solution determines the maximum number of fish that can exist in a connected region of a grid, using a union-find data structure. The solution involves several main steps:

  1. Initialization:

    • Define arrays for set roots, set sizes, and fish counts based on the grid's dimensions.
    • Initialize each grid cell as its own set, with size initialized to 1 and fish count derived from the grid's values.
  2. Union-Find Setup:

    • Include helper functions to find the root of a set and to merge two sets:
      • findSetRoot employs path compression to reduce the tree height, speeding up subsequent find operations.
      • mergeSets uses union by size strategy, ensuring that the smaller set points to the root of the larger set, thereby balancing the tree.
  3. Graph Navigation:

    • Navigate through each cell of the grid. For each fish-positive cell, check its four possible neighbors (up, down, left, right).
    • If a neighboring cell is within bounds and also contains fish, merge the current cell’s set with the neighbor’s set.
  4. Compute Maximum Fish Count:

    • Finally, iterate over all cells to find sets that are their own roots and determine the maximum fish count among them by comparing all root fish counts.

This approach ensures efficient calculations by minimizing the depth of sets in the union-find structure and quickly combining adjacent sets with fish to find the largest connected group of cells. The use of path compression and union by size heuristics in the union-find operations helps in maintaining almost flat trees, which significantly speeds up the union and find operations. Thus, the solution effectively tackles the problem by systematically organizing and merging sets based on the grid configurations.

python
class Solution:
    def maximumFishInGrid(self, aquatic_grid):
        def root_find(current_node):  # Locate the root parent of a node
            if disjoint_set[current_node] != current_node:
                disjoint_set[current_node] = root_find(
                    disjoint_set[current_node]
                )  # Path compression for efficiency
            return disjoint_set[current_node]
    
        def merge_components(node1, node2):  # Union two sets
            root1 = root_find(node1)
            root2 = root_find(node2)
            if root1 != root2:
                # Heuristic union by size
                if size[root1] < size[root2]:
                    root1, root2 = root2, root1
                disjoint_set[root2] = root1
                size[root1] += size[root2]
                fish_count[root1] += fish_count[root2]
    
        numRows, numCols = len(aquatic_grid), len(aquatic_grid[0])
        total_nodes = numRows * numCols
    
        # Set up data structures for Union-Find
        disjoint_set = list(range(total_nodes))
        size = [1] * total_nodes
        fish_count = [0] * total_nodes
    
        # Initializing fish count based on the grid input
        for r in range(numRows):
            for c in range(numCols):
                index = r * numCols + c
                fish_count[index] = aquatic_grid[r][c]
    
        # Directions for checking neighboring cells (right, left, down, up)
        dr = [0, 0, 1, -1]
        dc = [1, -1, 0, 0]
    
        # Perform Union-Find merge operations
        for r in range(numRows):
            for c in range(numCols):
                if aquatic_grid[r][c] > 0:  # Only consider cells with fish
                    index = r * numCols + c
                    for d in range(4):
                        new_r = r + dr[d]
                        new_c = c + dc[d]
                        if (
                            0 <= new_r < numRows
                            and 0 <= new_c < numCols
                            and aquatic_grid[new_r][new_c] > 0
                        ):
                            neighbor_index = (
                                new_r * numCols + new_c
                            )
                            merge_components(index, neighbor_index)
    
        # Compute maximum fish count among all sets
        max_fish = 0
        for index in range(total_nodes):
            if (
                root_find(index) == index
            ):  # Only consider root nodes
                max_fish = max(max_fish, fish_count[index])
    
        return max_fish

The solution for the "Maximum Number of Fish in a Grid" problem involves determining the largest possible number of fish that can be gathered from a grid where each cell represents a different number of fish, and cells with positive numbers of fish are potentially connected. The Python code implements this using a Union-Find algorithm, optimized with path compression and union by size heuristics.

Here's a detailed explanation of the provided solution:

  • Data Structures Initialization:

    • disjoint_set array is used to track the root of each node.
    • size array tracks the size of each component for union by size.
    • fish_count array holds the number of fish in the component of each cell's root.
  • Grid Setup: The grid is processed to initialize the fish_count for each cell based on provided fish numbers.

  • Union-Find Operations:

    • Cells are connected conditionally; only if both have fish and are neighbors. Four possible neighbor directions are considered: right, left, down, and up.
    • When two nodes belong to different sets, they are merged. The smaller set (by size) is merged into the bigger set, and fish counts are accumulated.
  • Calculating Maximum Fish Count: After all possible mergers, traverse through each root node to determine the maximum fish count among all disjoint sets.

The complete operation ensures components containing fish are merged optimally to maximize the total fish count in any connected area of the grid. The method returns the maximum possible fish count that can be obtained by merging adjacent cells containing fish. This is a typical use of the Union-Find data structure to tackle connectivity problems efficiently in a grid-like structure.

Comments

No comments yet.