947. 移除最多的同行或同列石头

lzyprime 博客 (github)
创建时间:2021.01.15
qq及邮箱:2383518170

leetcode 笔记


题目描述

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] 是平面上唯一一块石头,所以不可以移除它。
 

提示:

1 <= stones.length <= 1000
0 <= xi, yi <= 104
不会有两块石头放在同一个坐标点上

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/most-stones-removed-with-same-row-or-column
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

code

并查集, 同行或同列的石头,可归为一族,每族只需要留1块石头。

  • c++

class Solution {
 private:
  unordered_map<int, int> par, lv;

  int find_parent(int x) {
    if (!par.count(x)) {
      lv[x] = 1;
      return par[x] = x;
    }
    return par[x] == x ? x : find_parent(par[x]);
  }

 public:
  int removeStones(vector<vector<int>>& stones) {
    for (auto& i : stones) {
      int p1 = find_parent(i[0]), p2 = find_parent(-i[1] - 1);
      if (p1 == p2) continue;
      if (lv[p1] < lv[p2]) swap(p1, p2);
      lv[p1] += lv.erase(p2);
      par[p2] = p1;
    }

    return stones.size() - lv.size();
  }
};

  • kotlin

class Solution {
    fun MutableMap<Int, Int>.findParent(x: Int): Int = if (this.getOrPut(x) { x } == x) x else findParent(this[x]!!)

    fun removeStones(stones: Array<IntArray>): Int =
        stones.size - stones.fold(mutableMapOf<Int, Int>()) { par, ints ->
            par[par.findParent(ints.first())] = par.findParent(-ints.last() - 1)
            par
        }.count { (k, v) -> k == v }
}
  • scala

import scala.annotation.tailrec
import scala.collection.mutable

object Solution {
  @tailrec
  private def findParent(par: mutable.Map[Int, Int], x: Int): Int = if (par.getOrElseUpdate(x, x) != x) findParent(par, par(x)) else x

  def removeStones(stones: Array[Array[Int]]): Int = 
    stones.length - stones.foldLeft(mutable.Map[Int, Int]())((par, i) => {
      par.update(findParent(par, i.head), findParent(par, -i.last - 1))
      par
    }).count(i => i._1 == i._2)
}
  • rust

use std::collections::HashMap;

impl Solution {
    pub fn remove_stones(stones: Vec<Vec<i32>>) -> i32 {
        fn findParent(par: &mut HashMap<i32, i32>, x: i32) -> i32 {
            if par.entry(x).or_insert(x).clone() != x {
                findParent(par, par[&x])
            } else {
                x
            }
        }

        let mut par = stones.iter().fold(HashMap::new(), |mut par, i| {
            let p1 = findParent(&mut par, i[0]);
            let p2 = findParent(&mut par, -i[1] - 1);
            par.insert(p2, p1);
            par
        });
        par.retain(|&k, &mut v| k == v);
        let ans = stones.len() as i32 - par.len() as i32;
        ans
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这道题目可以使用二分答案的思想来解决。首先,我们可以确定一个最小距离的下界,即首尾两块石头之间的距离除以m+1,因为最多移除m块石头,所以剩下的石头之间的距离至少为这个值。然后,我们可以二分一个最小距离的上界,然后判断是否能够移除m块石头,使得相邻两块石头之间的距离都大于等于这个最小距离。如果可以,那么我们可以继续往上二分,否则就往下二分,直到找到最大的最小距离。 ### 回答2: 这道问题涉及到的知识点是贪心算法,通过贪心策略找到最优解。 首先需要明确的是,移除石头的数量越多,留下的石头之间的距离就会越大,相邻石头之间的距离的最小值就会越小;反之,移除石头越少,相邻石头之间的距离的最小值就会越大。 因此,我们需要寻找一个最优的移除石头数量,使得相邻石头之间的距离的最小值最大。为了达到这个目标,我们可以使用二分查找来确定移除石头数量,二分查找的上下界分别是1和石头的总数量减去2(首尾两块石头不能被移除)。 在每一次查找中,我们需要判断是否存在一种方案可以移除m块石头后,相邻石头之间的距离的最小值大于等于当前二分值mid。为了达到这个目标,我们可以从链的起点开始,逐个查找石头,如果两个相邻石头之间的距离小于mid,则移除其中一个石头,直到没有可以移除石头为止。 如果在移除m块石头后,相邻石头之间的距离的最小值大于等于mid,则表示当前二分值可以满足题目条件,我们需要继续查找更大的二分值;否则,我们需要查找更小的二分值。最终,二分查找结束后,找到的就是最大的相邻石头之间距离的最小值。 总之,这道问题的解法是贪心算法+二分查找,通过寻找最优的移除石头数量来达到相邻石头之间距离的最小值最大。 ### 回答3: 题目分析: 这个问题可以看作是一个二分答案的问题,即我们可以二分答案得到最大的相邻两块石头之间的距离。然后判断在这个距离前提下,是否可以移除 m 块石头。如果可以,则说明最大距离偏小,我们应该让最大距离增大;否则说明最大距离偏大,我们应该让最大距离减小。 具体实现: - 对给定的相邻两块石头之间的距离进行排序,我们可以使用 STL 中的 sort 函数进行排序。 - 判断这个最小值为 val 时,能否移除 m 块石头。因为首尾两块石头不能移除,所以可以从第二块石头开始进行贪心,只要有连续的 m+1 块石头距离都小于等于 val,就选择移除第 m+1 块石头。 - 最后我们可以进行二分答案,在搜索的过程中,如果在 val 下可以移除 m 块石头,则说明答案可以更大;反之则说明答案应该更小。 代码实现: ```C++ #include <algorithm> #include <iostream> using namespace std; const int N = 1e5 + 5; int n, m; int dist[N], diff[N]; bool check(int mid) { int cnt = 0; for (int i = 1, j = 1; i <= n; j = i) { while (j + 1 <= n && dist[j + 1] - dist[i] <= mid) ++j; if (j == i) return false; if (i != 1 || j != n) ++cnt; i = j + 1; } return cnt <= m; } int main() { cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> dist[i]; sort(dist + 1, dist + n + 1); for (int i = 2; i <= n; ++i) diff[i - 1] = dist[i] - dist[i - 1]; int l = 0, r = 1e9 + 1; while (r - l > 1) { int mid = (l + r) / 2; if (check(mid)) l = mid; else r = mid; } cout << l << endl; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值