Number of Enclaves

Here is the problem (medium): https://leetcode.com/problems/number-of-enclaves/

Given a 2D array A, each cell is 0 (representing sea) or 1 (representing land)
A move consists of walking from one land square 4-directionally to another land square, or off the boundary of the grid.
Return the number of land squares in the grid for which we cannot walk off the boundary of the grid in any number of moves.

Example 1:
Input: [[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
Output: 3
Explanation: 
There are three 1s that are enclosed by 0s, and one 1 that isn't enclosed because its on the boundary.
Example 2:
Input: [[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,0,0]]
Output: 0
Explanation: 
All 1s are either on the boundary or can reach the boundary.

Note:
  1. 1 <= A.length <= 500
  2. 1 <= A[i].length <= 500
  3. 0 <= A[i][j] <= 1
  4. All rows have the same size.

Here is one way to solve it:
a) We'll do a Breadth-First-Search (BFS) using a Queue
b) Have a helper method which does the BFS returning the number of connected lands, AND whether they reach the boundaries of the matrix
c) In the outer-loop (outer-function), whenever the answer to the helper method is "no", then add the count to the total results

That's it. Solution below, and so is what happens when you search for "Thanos" on Bing! Cheers, ACC.




public class Solution
{
 public int NumEnclaves(int[][] A)
 {
  int result = 0;
  Hashtable visited = new Hashtable();

  for (int r = 0; r < A.Length; r++)
  {
   for (int c = 0; c < A[0].Length; c++)
   {
    if (A[r][c] == 1)
    {
     int count = 0;
     if (!CanWalkOffBoundary(A, r, c, ref visited, ref count))
     {
      result += count;
     }
    }
   }
  }

  return result;
 }

 private bool CanWalkOffBoundary(int[][] A,
         int row,
         int col,
         ref Hashtable visited,
         ref int count)
 {
  bool canWalkOff = false;
  count = 0;

  Queue queue = new Queue();
  Cell cell = null;

  if (!visited.ContainsKey(row.ToString() + "@" + col.ToString()))
  {
   cell = new Cell(row, col);
   queue.Enqueue(cell);
   visited.Add(row.ToString() + "@" + col.ToString(), true);
  }

  while (queue.Count > 0)
  {
   cell = (Cell)queue.Dequeue();

   if (cell.row - 1 < 0 ||
    cell.row + 1 >= A.Length ||
    cell.col - 1 < 0 ||
    cell.col + 1 >= A[0].Length)
   {
    canWalkOff = true;
   }
   count++;
   int[] rowDelta = { -1, 1, 0, 0 };
   int[] colDelta = { 0, 0, -1, 1 };

   for (int i = 0; i < rowDelta.Length; i++)
   {
    int r = cell.row + rowDelta[i];
    int c = cell.col + colDelta[i];

    if (r >= 0 &&
     r < A.Length &&
     c >= 0 &&
     c < A[0].Length &&
     A[r][c] == 1 &&
     !visited.ContainsKey(r.ToString() + "@" + c.ToString()))
    {
     queue.Enqueue(new Cell(r, c));
     visited.Add(r.ToString() + "@" + c.ToString(), true);
    }
   }
  }

  return canWalkOff;
 }
}

public class Cell
{
 public int row = 0;
 public int col = 0;

 public Cell(int row,
    int col)
 {
  this.row = row;
  this.col = col;
 }
}

Comments

  1. I have decided to approach this problem from a different angle - I imagined boundary cells as roots in GC. So I add all border cells to the queue and perform a "mark" phase by discovering all reachable nodes and in order to not process them more than once, I set their values to 0. Once this is done, all that is left is to count the number of 1s left (garbage in GC terminology).

    from collections import deque
    import itertools

    DIRECTIONS = (-1, 0, 1, 0, -1)

    class Solution:
    def numEnclaves(self, A: List[List[int]]) -> int:
    q = deque(itertools.chain(((row, col) for row in (0, len(A)-1) for col in range(len(A[row])) if A[row][col] == 1), ((row, col) for col in (0, len(A[0])-1) for row in range(1, len(A)-1) if A[row][col] == 1)))
    for x, y in q:
    A[x][y] = 0 # mark seen
    while q:
    x, y = q.popleft()
    for x_dir, y_dir in zip(DIRECTIONS, DIRECTIONS[1:]):
    new_x, new_y = x + x_dir, y + y_dir
    if not (0 <= new_x < len(A) and 0 <= new_y < len(A[new_x])) or A[new_x][new_y] != 1:
    continue
    A[new_x][new_y] = 0
    q.append((new_x, new_y))
    return sum(row.count(1) for row in A)

    Full code: https://gist.github.com/ttsugriy/cbc457c9e0143a73401fe50fab33ccf3

    The code can be further simplified by using implicit DFS (recursion), but it's too easy :)

    ReplyDelete

Post a Comment

Popular posts from this blog

Golang vs. C#: performance characteristics (simple case study)

Claude vs ChatGPT: A Coder's Perspective on LLM Performance

ProjectEuler Problem 719 (some hints, but no spoilers)