<p>You are given a <strong>0-indexed</strong> 2D matrix <code>grid</code> of size <code>m x n</code>, where <code>(r, c)</code> represents:</p>

<ul>
	<li>A <strong>land</strong> cell if <code>grid[r][c] = 0</code>, or</li>
	<li>A <strong>water</strong> cell containing <code>grid[r][c]</code> fish, if <code>grid[r][c] &gt; 0</code>.</li>
</ul>

<p>A fisher can start at any <strong>water</strong> cell <code>(r, c)</code> and can do the following operations any number of times:</p>

<ul>
	<li>Catch all the fish at cell <code>(r, c)</code>, or</li>
	<li>Move to any adjacent <strong>water</strong> cell.</li>
</ul>

<p>Return <em>the <strong>maximum</strong> number of fish the fisher can catch if he chooses his starting cell optimally, or </em><code>0</code> if no water cell exists.</p>

<p>An <strong>adjacent</strong> cell of the cell <code>(r, c)</code>, is one of the cells <code>(r, c + 1)</code>, <code>(r, c - 1)</code>, <code>(r + 1, c)</code> or <code>(r - 1, c)</code> if it exists.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2023/03/29/example.png" style="width: 241px; height: 161px;" />
<pre>
<strong>Input:</strong> grid = [[0,2,1,0],[4,0,0,3],[1,0,0,4],[0,3,2,0]]
<strong>Output:</strong> 7
<strong>Explanation:</strong> The fisher can start at cell <code>(1,3)</code> and collect 3 fish, then move to cell <code>(2,3)</code>&nbsp;and collect 4 fish.
</pre>

<p><strong class="example">Example 2:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2023/03/29/example2.png" />
<pre>
<strong>Input:</strong> grid = [[1,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,1]]
<strong>Output:</strong> 1
<strong>Explanation:</strong> The fisher can start at cells (0,0) or (3,3) and collect a single fish. 
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>m == grid.length</code></li>
	<li><code>n == grid[i].length</code></li>
	<li><code>1 &lt;= m, n &lt;= 10</code></li>
	<li><code>0 &lt;= grid[i][j] &lt;= 10</code></li>
</ul>

class UnionFind:
    def __init__(self, rows, cols, grid):
        self.parent = {}
        self.size = [[0 for _ in range(cols)] for _ in range(rows)]

        for i in range(rows):
            for j in range(cols):
                self.parent[(i,j)] = (i, j)
                self.size[i][j] = grid[i][j]
    
    def find(self, x: tuple):
        if x == self.parent[x]:
            return x
        self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, x:tuple, y:tuple):
        r1,c1 = self.find(x)
        r2,c2 = self.find(y)
        
        if (r1,c1) != (r2,c2):
            if self.size[r1][c1] > self.size[r2][c2]:
                self.parent[(r2,c2)] = (r1,c1)
                self.size[r1][c1] += self.size[r2][c2]
            else:
                self.parent[(r1,c1)] = (r2,c2)
                self.size[r2][c2] += self.size[r1][c1]


class Solution:
    def findMaxFish(self, grid: List[List[int]]) -> int:

        def connect(i,j):
            if valid(i, j + 1):
                dsu.union((i,j), (i, j + 1))
            if valid(i, j - 1):
                dsu.union((i,j), (i, j - 1))
            if valid(i + 1, j):
                dsu.union((i,j), (i + 1, j))
            if valid(i - 1, j):
                dsu.union((i,j), (i - 1, j))

        def valid(r,c):
            if r < 0 or c < 0 or r >= rows or c >= cols or grid[r][c] == 0:
                return False
            return True

        rows,cols = len(grid), len(grid[0])
        dsu = UnionFind(rows, cols, grid)

        for i in range(rows):
            for j in range(cols):
                if valid(i,j):
                    connect(i,j)
        
        return max([max(row) for row in dsu.size])
266