Given an 2D array of non-negative integers stones[][] where stones[i] = [xi, yi] represents the location of theith stone on a 2D plane, return the maximum possible number of stones that you can remove.
A stone can be removed if it shares either the same row or same column as another stone that has not been removed.
Note: Each coordinate point in the grid can have at most one stone.
Output: 5 Explanation: 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 any row/column with another stone still on the plane.
Output: 3 Explanation: One way to remove 3 stones 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 any row/column with another stone still on the plane.
A stone can be removed if there is at least one other stone in the same row or column.
Any two stones in the same row or column are connected by an edge.
By repeatedly merging the stones connected by an edge, we can form a connected component.
To maximize the number of stones removed, we repeatedly remove the stone that has the fewest dependencies, i.e., the stone with the minimum degree. Removing such a stone minimizes the impact on the overall structure. Once a stone is removed, all edges connected to it (representing row/column relationships with other stones) are also removed, and the degrees of the affected stones are updated accordingly. We remove stones as long as each one still shares a row or column with another. When a stone no longer has any such neighbor, it canât be removed, and the process stops.
Further Observation:
Here we observe that in each such component, all stones except one can be removed, because the final remaining stone has no other stone in its row or column to justify its removal.
Maximum number of stones removed = total number of stones â number of connected components.
In order to find the number of connected components in the graph, we can do it by two approaches:
[Expected Approach - 1] - Using DSU
We use a Disjoint Set Union (DSU) structure to group together stones that lie in the same row or column. For every pair of stones, we check whether they share a row or column, and if they do, we merge them in the DSU. This process forms connected components of stones. After all unions are performed, the number of connected components can be determined by counting the distinct parent representatives in the DSU.
C++
//Driver Code Starts#include<iostream>#include<vector>#include<unordered_set>usingnamespacestd;//Driver Code Ends// find parent of the component a stone lies inintfindParent(inti,vector<int>&par){if(par[i]==i)returni;returnpar[i]=findParent(par[i],par);}// merging components based on ranksvoidunionSet(intu,intv,vector<int>&par,vector<int>&rank){intpu=findParent(u,par);intpv=findParent(v,par);// if both lie in same component, returnif(pu==pv)return;if(rank[pu]==rank[pv]){par[pu]=pv;rank[pv]++;}elseif(rank[pu]>rank[pv]){par[pv]=pu;}else{par[pu]=pv;}}intmaxRemove(vector<vector<int>>&stones){intn=stones.size();// parent denotes the parent node // of the component a stone lies invector<int>par(n),rank(n,0);// initially each stone is in a different componentfor(inti=0;i<n;i++){par[i]=i;}// for each pair of stones, we check if // they are in the same row or column// in order to merge them for(inti=0;i<n;i++){for(intj=i+1;j<n;j++){// to check for same row or columnif(stones[i][0]==stones[j][0]||stones[i][1]==stones[j][1]){unionSet(i,j,par,rank);}}}// set to include different components// each having a different parentunordered_set<int>components;for(inti=0;i<n;i++){components.insert(findParent(i,par));}// atleast 1 stone per component // cannot be removedreturnn-components.size();}//Driver Code Startsintmain(){vector<vector<int>>stones={{0,0},{0,2},{1,1},{2,0},{2,2}};cout<<maxRemove(stones);}//Driver Code Ends
Java
//Driver Code Startsimportjava.util.HashSet;classGFG{//Driver Code Ends// find parent of the component a stone lies instaticintfindParent(inti,int[]par){if(par[i]==i)returni;returnpar[i]=findParent(par[i],par);}// merging components based on ranksstaticvoidunion(intu,intv,int[]par,int[]rank){intpu=findParent(u,par);intpv=findParent(v,par);// if both lie in same component, returnif(pu==pv)return;// merging components based on ranksif(rank[pu]==rank[pv]){par[pu]=pv;rank[pv]++;}elseif(rank[pu]>rank[pv]){par[pv]=pu;}else{par[pu]=pv;}}staticintmaxRemove(int[][]stones){intn=stones.length;// parent denotes the parent node // of the component a stone lies inint[]par=newint[n];int[]rank=newint[n];// initially each stone is in a different componentfor(inti=0;i<n;i++){par[i]=i;}// for each pair of stones, we check if // they are in the same row or column// in order to merge them for(inti=0;i<n;i++){for(intj=i+1;j<n;j++){// to check for same row or columnif(stones[i][0]==stones[j][0]||stones[i][1]==stones[j][1]){union(i,j,par,rank);}}}// set to include different components// each having a different parentHashSet<Integer>components=newHashSet<>();for(inti=0;i<n;i++){components.add(findParent(i,par));}// atleast 1 stone per component // cannot be removedreturnn-components.size();}//Driver Code Startspublicstaticvoidmain(String[]args){int[][]stones={{0,0},{0,2},{1,1},{2,0},{2,2}};System.out.println(maxRemove(stones));}}//Driver Code Ends
Python
# find parent of the component a stone lies indeffindParent(i,par):ifpar[i]==i:returnipar[i]=findParent(par[i],par)returnpar[i]# merging components based on ranksdefunion(u,v,par,rank):pu=findParent(u,par)pv=findParent(v,par)# if both lie in same component, returnifpu==pv:return# merging components based on ranksifrank[pu]==rank[pv]:par[pu]=pvrank[pv]+=1elifrank[pu]>rank[pv]:par[pv]=puelse:par[pu]=pvdefmaxRemove(stones):n=len(stones)# parent denotes the parent node # of the component a stone lies inpar=list(range(n))rank=[0]*n# initially each stone is in a different componentforiinrange(n):par[i]=i# for each pair of stones, we check if # they are in the same row or columnforiinrange(n):forjinrange(i+1,n):# to check for same row or columnifstones[i][0]==stones[j][0]orstones[i][1]==stones[j][1]:union(i,j,par,rank)components=set(findParent(i,par)foriinrange(n))returnn-len(components)#Driver Code Startsif__name__=="__main__":stones=[[0,0],[0,2],[1,1],[2,0],[2,2]]print(maxRemove(stones))#Driver Code Ends
C#
//Driver Code StartsusingSystem;usingSystem.Collections.Generic;classGFG{//Driver Code Ends// find parent of the component a stone lies instaticintfindParent(inti,int[]par){if(par[i]==i)returni;returnpar[i]=findParent(par[i],par);}// merging components based on ranksstaticvoidunion(intu,intv,int[]par,int[]rank){intpu=findParent(u,par);intpv=findParent(v,par);// if both lie in same component, returnif(pu==pv)return;// merging components based on ranksif(rank[pu]==rank[pv]){par[pu]=pv;rank[pv]++;}elseif(rank[pu]>rank[pv]){par[pv]=pu;}else{par[pu]=pv;}}staticintmaxRemove(int[][]stones){intn=stones.Length;// parent denotes the parent node // of the component a stone lies inint[]par=newint[n];int[]rank=newint[n];// initially each stone is in a different componentfor(inti=0;i<n;i++)par[i]=i;// for each pair of stones, we check if // they are in the same row or columnfor(inti=0;i<n;i++){for(intj=i+1;j<n;j++){// to check for same row or columnif(stones[i][0]==stones[j][0]||stones[i][1]==stones[j][1]){union(i,j,par,rank);}}}// set to include different componentsHashSet<int>components=newHashSet<int>();for(inti=0;i<n;i++){components.Add(findParent(i,par));}// atleast 1 stone per component // cannot be removedreturnn-components.Count;}//Driver Code StartsstaticvoidMain(){int[][]stones=newint[][]{newint[]{0,0},newint[]{0,2},newint[]{1,1},newint[]{2,0},newint[]{2,2}};Console.WriteLine(maxRemove(stones));}}//Driver Code Ends
JavaScript
// find parent of the component a stone lies infunctionfindParent(i,par){if(par[i]===i)returni;returnpar[i]=findParent(par[i],par);}// merging components based on ranksfunctionunionSet(u,v,par,rank){letpu=findParent(u,par);letpv=findParent(v,par);// if both lie in same component, returnif(pu===pv)return;// merging components based on ranksif(rank[pu]===rank[pv]){par[pu]=pv;rank[pv]++;}elseif(rank[pu]>rank[pv]){par[pv]=pu;}else{par[pu]=pv;}}functionmaxRemove(stones){letn=stones.length;// parent denotes the parent node // of the component a stone lies inletpar=Array(n).fill(0).map((_,i)=>i);letrank=Array(n).fill(0);// initially each stone is in a different componentfor(leti=0;i<n;i++)par[i]=i;// for each pair of stones, we check if // they are in the same row or column// in order to merge them for(leti=0;i<n;i++){for(letj=i+1;j<n;j++){// to check for same row or columnif(stones[i][0]===stones[j][0]||stones[i][1]===stones[j][1]){unionSet(i,j,par,rank);}}}// set to include different componentsletcomponents=newSet();for(leti=0;i<n;i++){components.add(findParent(i,par));}// atleast 1 stone per component // cannot be removedreturnn-components.size;}//Driver Code Starts// Driver codeletstones=[[0,0],[0,2],[1,1],[2,0],[2,2]];console.log(maxRemove(stones));//Driver Code Ends
Output
3
Time complexity: O(n2 + n log n), n2 is because we check for each pair of stone if they are in the same row or column, n log n because for all stones, we call the findParent function that takes O(log n) time. Auxiliary Space: O(n) for parent and rank array.
[Expected Approach - 2] - Using DFS
In this approach, for every unvisited stone, we start a DFS to explore all stones reachable through these row/column connections. This DFS recursively visits every stone that lies in the same row or column, effectively capturing one entire connected group. Once a DFS finishes, we know one full component is visited and therefore the number of DFS calls made are equal to the number of components in the graph. Since each component must keep one stone, the maximum removable stones are total number of stones â number of components.
C++
//Driver Code Starts#include<iostream>#include<vector>usingnamespacestd;//Driver Code Endsvoiddfs(inti,vector<bool>&v,vector<vector<int>>&stones){if(v[i])return;v[i]=true;for(intj=0;j<stones.size();j++){// if another stone has same row or // column as this stone then both lie // in the same componentif(stones[i][0]==stones[j][0]||stones[i][1]==stones[j][1]){dfs(j,v,stones);}}}intmaxRemove(vector<vector<int>>&stones){intn=stones.size();vector<bool>visited(n,false);intcomponents=0;for(inti=0;i<n;i++){// visiting the stone if not visited// and finding all the stones lying in // the same component as this stoneif(!visited[i]){dfs(i,visited,stones);components++;}}// atleast 1 stone per component // cannot be removedreturnn-components;}//Driver Code Startsintmain(){vector<vector<int>>stones={{0,0},{0,2},{1,1},{2,0},{2,2}};cout<<maxRemove(stones);}//Driver Code Ends
Java
//Driver Code StartsclassGFG{//Driver Code EndsstaticintmaxRemove(int[][]stones){intn=stones.length;boolean[]visited=newboolean[n];intcomponents=0;for(inti=0;i<n;i++){// visiting the stone if not visited// and finding all the stones lying in // the same component as this stoneif(!visited[i]){dfs(i,visited,stones);components++;}}// atleast 1 stone per component // cannot be removedreturnn-components;}staticvoiddfs(inti,boolean[]v,int[][]stones){if(v[i])return;v[i]=true;for(intj=0;j<stones.length;j++){// if another stone has same row or // column as this stone then both lie // in the same componentif(stones[i][0]==stones[j][0]||stones[i][1]==stones[j][1]){dfs(j,v,stones);}}}//Driver Code Startspublicstaticvoidmain(String[]args){int[][]stones={{0,0},{0,2},{1,1},{2,0},{2,2}};System.out.println(maxRemove(stones));}}//Driver Code Ends
Python
defdfs(i,visited,stones):ifvisited[i]:returnvisited[i]=Trueforjinrange(len(stones)):# if another stone has same row or # column as this stone then both lie # in the same componentifstones[i][0]==stones[j][0]orstones[i][1]==stones[j][1]:dfs(j,visited,stones)defmaxRemove(stones):n=len(stones)visited=[False]*ncomponents=0foriinrange(n):# visiting the stone if not visited# and finding all the stones lying in # the same component as this stoneifnotvisited[i]:dfs(i,visited,stones)components+=1# atleast 1 stone per component # cannot be removedreturnn-components#Driver Code Startsif__name__=="__main__":stones=[[0,0],[0,2],[1,1],[2,0],[2,2]]print(maxRemove(stones))#Driver Code Ends
C#
//Driver Code StartsusingSystem;usingSystem.Collections.Generic;classGFG{//Driver Code Endsstaticvoiddfs(inti,bool[]v,int[][]stones){if(v[i])return;v[i]=true;for(intj=0;j<stones.Length;j++){// if another stone has same row or // column as this stone then both lie // in the same componentif(stones[i][0]==stones[j][0]||stones[i][1]==stones[j][1]){dfs(j,v,stones);}}}staticintmaxRemove(int[][]stones){intn=stones.Length;bool[]visited=newbool[n];intcomponents=0;for(inti=0;i<n;i++){// visiting the stone if not visited// and finding all the stones lying in // the same component as this stoneif(!visited[i]){dfs(i,visited,stones);components++;}}// atleast 1 stone per component // cannot be removedreturnn-components;}//Driver Code StartsstaticvoidMain(){int[][]stones=newint[][]{newint[]{0,0},newint[]{0,2},newint[]{1,1},newint[]{2,0},newint[]{2,2}};Console.WriteLine(maxRemove(stones));}}//Driver Code Ends
JavaScript
functiondfs(i,visited,stones){if(visited[i])return;visited[i]=true;for(letj=0;j<stones.length;j++){// if another stone has same row or // column as this stone then both lie // in the same componentif(stones[i][0]===stones[j][0]||stones[i][1]===stones[j][1]){dfs(j,visited,stones);}}}functionmaxRemove(stones){letn=stones.length;letvisited=newArray(n).fill(false);letcomponents=0;for(leti=0;i<n;i++){// visiting the stone if not visited// and finding all the stones lying in // the same component as this stoneif(!visited[i]){dfs(i,visited,stones);components++;}}// atleast 1 stone per component // cannot be removedreturnn-components;}//Driver Code Starts// Driver codeletstones=[[0,0],[0,2],[1,1],[2,0],[2,2]];console.log(maxRemove(stones));//Driver Code Ends
Output
3
Time complexity: O(n2)because in the worst case each stone can lie in a unique component, and therefore we make dfs call for each stone, each dfs call takes O(n) because we check if any stone shares the same row or column as current stone. Auxiliary Space: O(n) for recursive stack and visited array.