其他分享
首页 > 其他分享> > 【力扣】一处最多的同行或同列石头

【力扣】一处最多的同行或同列石头

作者:互联网

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/most-stones-removed-with-same-row-or-column

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] 不能移除,因为它们没有与另一块石头同行/列。

思路并查集,把石头坐标看成图的连线,如果这些点链接起来寻找祖先,最后把字典中的祖先节点个数数一下就是不能被替换掉的节点,用总长度减去不能被换替换掉的节点个数就得答案。可惜是错的。

 1 class Solution:
 2     def removeStones( stones) -> int:
 3         f = {}
 4         n = len(stones)
 5 
 6         def find(x):
 7             f.setdefault(x, x)
 8             if x != f[x]:
 9                 f[x] = find(f[x])
10             return f[x]
11 
12         def union(x, y):
13             f[find(y)] = find(x)
14 
15         for i in range(n):
16             if find(stones[i][0]) != find(stones[i][1]):
17                 union(stones[i][0], stones[i][1])
18         print(f)
19         return n - len(set(f.values()))

正确思路:

作者:yim-6
链接:https://leetcode-cn.com/problems/most-stones-removed-with-same-row-or-column/solution/python3-bing-cha-ji-lian-jie-heng-zong-z-e3kc/

解题思路
参数定义
n:x最大取值
parent:并查集数组,存储祖先节点
root:哈希表,存储每个连通区域的根节点
思路
由于x,y所属区间为$[0,10^4]$,避免越界,可取n为10010

关键在于union(i,j+n)的理解:

  1.反例:如果是union(i,j),则可能混淆i,j。如(2,4)和(4,1)不在同行同列,而通过union(i,j)后就成了一个连通区域。

  2.如图union(i,j+n),示例(0,0)和(2,0)会指向10010,而(0,2)和(2,2)会指向10012,而由于连通性,(2,0)和(2,2)有着相同的横坐标,所以10010指向10012

遍历stones中的x坐标,可以找到两个连通区域的根节点:10011和10012

返回值即为:石头总数-连通区域个数

复杂度分析 
时间复杂度:O(Mα(M)),M为石头数量,α(M)为路径压缩时间
空间复杂度:O(N),parent的大小

 1 class Solution:
 2     def removeStones(self, stones: List[List[int]]) -> int:
 3         n=10010
 4         parent=list(range(2*n))
 5         # 并查集查找
 6         def find(x):
 7             if x!=parent[x]:
 8                 parent[x]=find(parent[x])
 9             return parent[x]
10         # 合并
11         def union(i,j):
12             parent[find(i)]=find(j)            
13         # 连通横纵坐标
14         for i,j in stones:
15             union(i,j+n)
16         # 获取连通区域的根节点
17         root=set()
18         for i,j in stones:
19             root.add(find(i))
20         return len(stones)-len(root)
21 
22 作者:yim-6
23 链接:https://leetcode-cn.com/problems/most-stones-removed-with-same-row-or-column/solution/python3-bing-cha-ji-lian-jie-heng-zong-z-e3kc/
24 来源:力扣(LeetCode)

DFS解法:

作者:heimisa000
链接:https://leetcode-cn.com/problems/most-stones-removed-with-same-row-or-column/solution/pythondfsjie-fa-yong-shi-jin-wei-68msda-liang-zhu-/

1. 思路:
1.1 题目分析:
首先吐槽一下题目,我左看右看也没看懂-_-||

题目含义是:如果想移除一个石头,那么它所在的行或者列必须有其他石头存在。我们能移除的最多石头数是多少?

也就是说,只有一个石头在连通分量中,才能被移除。对于连通分量而言,最理想的状态是只剩一块石头。对于任何容量为n一个连通分量,可以移除的石头数都为n-1。

那么问题就被转化为:

这些石头可以构成多少个连通分量,每个分量中有多少块石头?

进一步地,由于题目中强调了,每个坐标点最多只能有一块石头,也就是说给定的stones列表中没有重复坐标,总石头数等于stones列表的长度。而每个连通分量最后都会剩下一块石头,所以:

可以移走的石头数 = 总石头数 - 剩余石头数 = stones列表长度 - 连通分量个数

1.2 连通分量计算:
事实上,连通分量计算最好的办法是并查集,可是我们这里偏要勉强,用DFS解法计算(实际上是渣渣并不懂并查集 ̄□ ̄||)。

值得注意的是,连通的条件是:两个点在同一行或者同一列,为了保存行和列信息,我们用两个set进行记录,即row和col。剩余部分与其他连通分量计算的题大同小异,详见代码。

 1 class Solution:
 2     def removeStones(self, stones: List[List[int]]) -> int:
 3         m = 0                                    # 记录最大的行坐标
 4         row = collections.defaultdict(set)       # 记录每一行中点的列坐标
 5         col = collections.defaultdict(set)       # 记录每一列中点的行坐标
 6 
 7         for i,j in stones:                       # 遍历所有石头坐标
 8             row[i] |= {j}                        # 记录行对应的列坐标
 9             col[j] |= {i}                        # 记录列对应的坐标
10             m = max(m,i)                         # 记录最大的行坐标
11 
12 
13         graph = 0                                # 连通分量的个数
14 
15         for i in range(m+1):                     # 对于所有的i进行遍历
16             if i in row:                         # 如果i在row中,即i对一个新的连通分量
17                 graph += 1                       # 连通分量数 +1
18                 stack = {i}                      # 这里用set模拟栈,方便后面合并操作
19 
20                 while stack:                     # 栈非空时
21                     x = stack.pop()              # 从栈中不断弹出行坐标x
22                     for j in row[x]:             # 对于row[x]中所保存的所有列坐标j
23                         if j in col:             # 如果col[j]非空,说明该列有对应的行
24                                                  # 可以和当前行x构成连通分量
25                             stack |= col[j]      # 将col[j]中所有的行合并到栈中
26                             del col[j]           # 并删除col[j],防止循环调用
27                     
28                     del row[x]                   # 最后删除row[x],防止循环调用
29 
30         return len(stones) - graph               # 针对于每个连通分量,最后只剩一个石子
31                                                  # 因此剩余石子个数为连通分量个数graph
32                                                  # 由于石子坐标是唯一的
33                                                  # 因此总石子个数为stones的长度
34                                                  # 可操作数为总石子数 - 剩余石子数
35                                                  # 即 len(stones) - graph
36 
37 
38 作者:heimisa000
39 链接:https://leetcode-cn.com/problems/most-stones-removed-with-same-row-or-column/solution/pythondfsjie-fa-yong-shi-jin-wei-68msda-liang-zhu-/
40 来源:力扣(LeetCode)

 

标签:stones,连通,同行,石头,力扣,同列,坐标,移除,row
来源: https://www.cnblogs.com/Harukaze/p/14281456.html