Leetcode: Most Stones Removed with Same Row or Column

题目

分析

乍看之下,有点摸不着头脑。稍微举几个简单的例子,就能发现其中的规律。
题目给出一个“相连”的概念,即行或者列相同,这里可以用坐标系来理解。
那么,首先分析一些基本情况:

  • 当没有石头“相连”,那么无法做出符合题意的操作,是0;
  • 只有2个石头“相连”,能做出1个操作;
  • 3个石头“相连”,就是2个操作;
  • 可见,如果一组有n个石头相连,那么操作数就是n-1
  • 假如有两组,一组2相连,一组3相连,它们互相之间并不干涉,最后结果就是分别能进行操作的数目相加;
  • 假设总共有m个互不干涉的组,那么操作数总共就是(n(0)-1)+(n(1)-1)+...+(n(m-1)-1),其中n(i)代表第i组的石头个数。因为总共的石头个数是给定的,假设为N,那么可得最后的操作数就是(n(0)+...+n(m-1))-m = N-m。也就是说,问题转化为求总共有多少个互不相干的组
  • 要解决这个问题,连通图就是一个很直接的想法。但问题来了,我们一般运用的连通图是1维的,这个是2维的,怎么弄?假如强行开2维连通图,需要解决很多小问题,不是一时半会能写出无bug代码的,因此不如尝试把2维转化为1维。
  • 根据题意,坐标的x和y是需要区分开来的,因为两个石头(x1,y1)与(x2,y2),x或者y相等属于相连,但x1=y2这种可不算。
  • 最简洁的做法,就是把y加上一个值,让x和y不会重合就好。题目里给的值是10000.
  • 构造图只需要连通单个石头本身的x与y+10000就行了。假如与之前某个石头“相连”,连通的时候自然会发现。
  • 到这里,问题就解决了,参考代码如下:
// T:O(NlogN) S:O(N)
class Solution {
    public int removeStones(int[][] stones) {
        int N = stones.length;
        DSU dsu = new DSU(20000);

        for (int[] stone: stones)
            dsu.union(stone[0], stone[1] + 10000);

        Set<Integer> seen = new HashSet();
        for (int[] stone: stones)
            seen.add(dsu.find(stone[0]));

        return N - seen.size();
    }
}

class DSU {
    int[] parent;
    public DSU(int N) {
        parent = new int[N];
        for (int i = 0; i < N; ++i)
            parent[i] = i;
    }
    public int find(int x) {
        if (parent[x] != x) parent[x] = find(parent[x]);
        return parent[x];
    }
    public void union(int x, int y) {
        parent[find(x)] = find(y);
    }
}