Taro Logo

Most Stones Removed with Same Row or Column

Medium
Google logo
Google
2 views
Topics:
GraphsDynamic ProgrammingGreedy Algorithms

On a 2D plane, we place n stones at some integer coordinate points. Each coordinate point may have at most one stone.

A stone can be removed if it shares either the same row or the same column as another stone that has not been removed.

Given an array stones of length n where stones[i] = [x<sub>i</sub>, y<sub>i</sub>] represents the location of the i<sup>th</sup> stone, return the largest possible number of stones that can be removed.

For example:

  1. stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]. The output should be 5. One way to remove 5 stones is as follows:

    • Remove stone [2,2] because it shares the same row as [2,1].
    • Remove stone [2,1] because it shares the same column as [0,1].
    • Remove stone [1,2] because it shares the same row as [1,0].
    • Remove stone [1,0] because it shares the same column as [0,0].
    • Remove stone [0,1] because it shares the same row as [0,0].

    Stone [0,0] cannot be removed since it does not share a row/column with another stone still on the plane.

  2. stones = [[0,0],[0,2],[1,1],[2,0],[2,2]]. The output should be 3. One way to make 3 moves is as follows:

    • Remove stone [2,2] because it shares the same row as [2,0].
    • Remove stone [2,0] because it shares the same column as [0,0].
    • Remove stone [0,2] because it shares the same row as [0,0].

    Stones [0,0] and [1,1] cannot be removed since they do not share a row/column with another stone still on the plane.

  3. stones = [[0,0]]. The output should be 0 because [0,0] is the only stone on the plane, so you cannot remove it.

How would you implement a function to solve this problem, and what is the time and space complexity of your solution?

Solution


Naive Approach: Brute Force

A straightforward approach would involve iterating through the stones and, for each stone, checking if there's another stone in the same row or column. If found, remove the current stone and repeat the process. This continues until no more stones can be removed.

Algorithm

  1. Create a copy of the input stones list to represent the current state of stones on the plane.
  2. Iterate through the stones in the copied list.
  3. For each stone, iterate through the remaining stones to check if any share the same row or column.
  4. If a stone sharing a row or column is found, remove the current stone from the copied list and increment a counter for removed stones.
  5. Repeat steps 2-4 until no more stones can be removed in an iteration.
  6. Return the counter representing the total number of removed stones.

Code (Python)

def remove_stones_brute_force(stones):
    n = len(stones)
    removed_count = 0
    
    while True:
        removed_in_iteration = False
        stones_to_remove = []
        
        for i in range(len(stones)):
            for j in range(len(stones)):
                if i != j and (stones[i][0] == stones[j][0] or stones[i][1] == stones[j][1]):
                    stones_to_remove.append(stones[i])
                    removed_count += 1
                    removed_in_iteration = True
                    break
            if removed_in_iteration:
                break
        
        if not removed_in_iteration:
            break
        
        stones = [stone for stone in stones if stone not in stones_to_remove]
            
    return removed_count

Time Complexity

O(n^3) in the worst case, where n is the number of stones. The nested loops iterate through the stones multiple times.

Space Complexity

O(n) to store the copy of the stones list.

Optimal Approach: Disjoint Set Union (DSU)

The problem can be modeled as a graph where each stone is a node, and an edge exists between two stones if they share the same row or column. The maximum number of removable stones is equal to the total number of stones minus the number of connected components in the graph.

We can use the Disjoint Set Union (DSU) data structure to efficiently find the number of connected components.

Algorithm

  1. Initialize a DSU data structure with n nodes, where n is the number of stones.
  2. Iterate through the stones and union stones that share the same row or column.
  3. Count the number of connected components (number of disjoint sets).
  4. Return n - number of connected components.

Code (Python)

class DSU:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])  # Path compression
        return self.parent[x]

    def union(self, x, y):
        root_x = self.find(x)
        root_y = self.find(y)
        if root_x != root_y:
            if self.rank[root_x] < self.rank[root_y]:
                self.parent[root_x] = root_y
            elif self.rank[root_x] > self.rank[root_y]:
                self.parent[root_y] = root_x
            else:
                self.parent[root_y] = root_x
                self.rank[root_x] += 1

def remove_stones_dsu(stones):
    n = len(stones)
    dsu = DSU(n)
    for i in range(n):
        for j in range(i + 1, n):
            if stones[i][0] == stones[j][0] or stones[i][1] == stones[j][1]:
                dsu.union(i, j)
    
    num_components = 0
    for i in range(n):
        if dsu.find(i) == i:
            num_components += 1
            
    return n - num_components

Time Complexity

O(n^2 * α(n)), where n is the number of stones and α(n) is the inverse Ackermann function, which grows very slowly and can be considered almost constant for practical input sizes. Thus, effectively O(n^2).

Space Complexity

O(n) to store the parent and rank arrays in the DSU data structure.

Edge Cases

  • Empty input: If the input stones list is empty, return 0.
  • Single stone: If there's only one stone, return 0 since it cannot be removed.
  • No shared rows or columns: If no stones share the same row or column, return 0.
  • All stones in the same row or column: If all stones are in the same row or column, all but one can be removed, so return n - 1.