[路飞]_前端算法第九十一弹-移除最多的同行或同列石头

113 阅读3分钟

n 块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。

如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。

给你一个长度为 n 的数组 stones ,其中 stones[i] = [xi, yi] 表示第 i 块石头的位置,返回 可以移除的石子 的最大数量。

示例 1:

输入:stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]
输出:5
解释:一种移除 5 块石头的方法如下所示:
1. 移除石头 [2,2] ,因为它和 [2,1] 同行。
2. 移除石头 [2,1] ,因为它和 [0,1] 同列。
3. 移除石头 [1,2] ,因为它和 [1,0] 同行。
4. 移除石头 [1,0] ,因为它和 [0,0] 同列。
5. 移除石头 [0,1] ,因为它和 [0,0] 同行。
石头 [0,0] 不能移除,因为它没有与另一块石头同行/列。

示例 2:

输入:stones = [[0,0],[0,2],[1,1],[2,0],[2,2]]
输出:3
解释:一种移除 3 块石头的方法如下所示:
1. 移除石头 [2,2] ,因为它和 [2,0] 同行。
2. 移除石头 [2,0] ,因为它和 [0,0] 同列。
3. 移除石头 [0,2] ,因为它和 [0,0] 同行。
石头 [0,0][1,1] 不能移除,因为它们没有与另一块石头同行/列。

示例 3:

输入:stones = [[0,0]]
输出:0
解释:[0,0] 是平面上唯一一块石头,所以不可以移除它。

本题经过仔细分析可以发现:这是一个图论的题,只要两个点处于同一行或者同一列,那么就可以删除其中一个点。

那么,我们应该尽可能晚地移除那些与其他点同行同列的点。也就是说与A点同行或同列的点数量越多,那么A应该越晚移除,因为这样,可以通过A点尽可能多得删除一些点。

我们怎么确定一些点是同行或者同列呢?

我们将同行或者同列的点连接起来,这样便构成了一个连通图。那么与点相连接的点必是同行或同列。

  • 首先,我们构造图:只要两个点同行或同列,那么将两个点相连接
  • 这样,最后的结果图应该是很多个连通图组成的非连通图
  • 而对于任何连通图,我们都可以从一端开始移除直至只剩下一个点
  • 所以,我们只需要判断有多少个连通图,最后便至少剩余多少个点
  • 最后,用节点的数量 - 连通图的数列即为结果

假如stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2],[3,3],[4,3],[3,5],[6,4]]最开始石子在坐标轴上的分布是这样:

然后将同行或同列的点连接起来,形成连通图:

然后针对每个连通图,从端点开始删除。如果存在环的话,任选一点开始删除:

最后每个连通图剩余一个节点:

代码

var removeStones = function (stones) {
  const len = stones.length
  let u = new UnionSet(len)
  const mapX = new Map()
  const mapY = new Map()
  for (let i = 0; i < len; i++) {
    const s = stones[i]
    if (mapX.has(s[0])) u.merge(i, mapX.get(s[0]))
    if (mapY.has(s[1])) u.merge(i, mapY.get(s[1]))
    mapX.set(s[0], i)
    mapY.set(s[1], i)
  }
  let res = 0
  for (let i = 0; i < len; i++) {
    if (u.get(i) == i) res++
  }
  return len - res;
};
class UnionSet {
  constructor(n) {
    this.fa = new Array(n + 1)
    this.n = n
    this.cnt = new Array(n + 1)
    for (let i = 0; i < n; i++) {
      this.fa[i] = i
      this.cnt[i] = 1
    }
  }
  get (x) {
    return this.fa[x] = (this.fa[x] == x ? x : this.get(this.fa[x]))
  }
  merge (a, b) {
    if (this.get(a) == this.get(b)) return
    this.cnt[this.get(b)] += this.cnt[this.get(a)]
    this.fa[this.get(a)] = this.get(b)
    return
  }
}