947.移除最多的同行或同列石头
思路一:并查集
其实这道题看似花里胡哨,实际上能移除的石头个数就是石头总个数减去属于同一类别(我们把处于同一列同一行能联通的石头放在一类)的石头个数。因为一坨互相联通的石头之间总可以移除到只剩下一块。
这个想法的证明可以倒过来想,如果只剩一块,根据我们分类的定义,只要没有达到该类别总个数,一定存在同一类的元素与其相连(处于同一行或者同一列),因此可以添加到两块,以此类推,就可以还原到最初状态。
又因为能加一块就一定能删一块,因此,我们证明了一坨互相联通的石头之间总可以移除到只剩下一块。
class Solution {
public int removeStones(int[][] stones) {
// 第一步,初始化每一块石头,这里由题目假设知,不会由两块石头放在同一个位置上
Stone[] st = new Stone[stones.length];
for(int i=0;i<stones.length;i++){
st[i] = new Stone(stones[i]);
// System.out.println(st[i].index);
}
// 第二步:将同行同列的石头合并在一起
UnionFind uf = new UnionFind(st.length);
for(int i=0;i<st.length;i++)
for(int j=i+1;j<st.length;j++){
if(st[i].sameline(st[j])){
uf.union(i,j);
//System.out.println(i+" "+j);
}
}
// 因为有多少个类别就能剩下多少个元素
return st.length-uf.count();
}
}
class Stone{
int[] coord;
int index;
static int total=0; // 用来记录石头总数
public Stone(int[] coord){
this.coord = coord;
this.index = index;
this.index = Stone.total++;
// System.out.println(this.index);
// System.out.println(Stone.total);
}
public boolean sameline(Stone other){
return this.coord[0]==other.coord[0] || this.coord[1]==other.coord[1];
}
}
class UnionFind{
int[] elements;
int[] rank;
public UnionFind(int N){
this.rank = new int[N];
this.elements = new int[N];
for(int i=0;i<N;i++)
this.elements[i]=i;
}
public int find(int i){
if(elements[i]==i)
return i;
else{
elements[i]=find(elements[i]);
return elements[i];
}
}
public void union(int i,int j){
int parenti = find(i);
int parentj = find(j);
if(rank[parenti]<rank[parentj])
elements[parenti]=parentj;
else if(rank[parenti]>rank[parentj])
elements[parentj]=parenti;
else{
elements[parentj]=parenti;
rank[parenti]++;
}
}
// 统计类别数量
public int count(){
int res=0;
for(int i=0;i<elements.length;i++)
if(elements[i]==i)
res++;
return res;
}
}