【LeetCode】947. Most Stones Removed with Same Row or Column 解題報告(Python & C++)
作者: 負雪明燭
id: fuxuemingzhu
個人部落格: http://fuxuemingzhu.cn/
目錄
題目地址:https://leetcode.com/problems/most-stones-removed-with-same-row-or-column/description/
題目描述
On a 2D plane, we place stones at some integer coordinate points. Each coordinate point may have at most one stone.
Now, a move consists of removing a stone that shares a column or row with another stone on the grid.
What is the largest possible number of moves we can make?
Example 1:
Input: stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]
Output: 5
Example 2:
Input: stones = [[0,0],[0,2],[1,1],[2,0],[2,2]] Output: 3
Example 3:
Input: stones = [[0,0]]
Output: 0
Note:
- 1 <= stones.length <= 1000
- 0 <= stones[i][j] < 10000
題目大意
在二維座標的整數座標點上,有一些石頭,如果一個石頭和另外一個石頭的橫座標或者縱座標相等,那麼認為他們是有連結的。我們每次取一個和別人有連結的石頭,問最終能取得多少個石頭。
解題方法
並查集
這個題翻譯一下就是,橫或者縱座標相等的座標點會互相連結構成一個區域,問總的有多少個獨立的區域。結果是總的石頭數減去獨立區域數。
所以,我們根本不用考慮太多,只需要統計有多少區域即可。這個方法最簡單的就是並查集。
思路是,兩重迴圈,分別判斷石頭兩兩之間是否有連結,如果有連結,那麼把他們組成同一個區域。這樣的優點是我們只需要和石頭等長的陣列放每個的parent即可。最後統計最後的區域中-1的數量就是獨立區域的個數。石頭個數減去獨立區域數即可。
python程式碼如下,可惜超時了。
class Solution(object):
def removeStones(self, stones):
"""
:type stones: List[List[int]]
:rtype: int
"""
N = len(stones)
self.map = [-1] * N
for i in range(N):
for j in range(i + 1, N):
if stones[i][0] == stones[j][0] or stones[i][1] == stones[j][1]:
self.union(i, j)
res = N
print(self.map)
for i in range(N):
if self.map[i] == -1:
res -= 1
return res
def find(self, x):
return x if self.map[x] == -1 else self.find(self.map[x])
def union(self, x, y):
fx = self.find(x)
fy = self.find(y)
if fx != fy:
self.map[fx] = fy
這個版本的C++可以做通過,說明了C++速度的優越性。
class Solution {
public:
int removeStones(vector<vector<int>>& stones) {
if(stones.size() <= 1) return 0;
int N = stones.size();
vector<int> p(N, -1);
for (int i = 0; i < N; ++i){
for(int j = i + 1; j < N; ++j){
if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]){
u(p, i, j);
}
}
}
int res = N;
for(auto e: p) if(e == -1) --res;
return res;
}
private:
int f(vector<int> &p, int x){
if(p[x] == -1) return x;
return f(p, p[x]);
}
void u(vector<int> &p, int x, int y){
int px = f(p, x);
int py = f(p, y);
if(px != py){
p[px] = py;
}
}
};
其實上面這個版本可以做優化,我們不用對石頭進行兩兩判斷,而是對他們的橫縱座標同等看待。怎麼區分橫縱座標呢?使用的方法是把縱座標+10000,這樣行的索引沒變,縱座標的範圍跑到了後面去了。
這個做法的思路是,一個座標其實就是把橫縱座標對應的兩個區域進行了連結。所以,只需要對stones進行一次遍歷把對應的區域連結到一起即可。在完成連結之後,我們最後統計一下有多少個獨立的區域,需要使用set+find。
Python程式碼如下:
class Solution(object):
def removeStones(self, stones):
"""
:type stones: List[List[int]]
:rtype: int
"""
N = len(stones)
self.map = [-1] * 20000
for x, y in stones:
self.union(x, y + 10000)
count = set()
return N - len({self.find(x) for x, y in stones})
def find(self, x):
return x if self.map[x] == -1 else self.find(self.map[x])
def union(self, x, y):
fx = self.find(x)
fy = self.find(y)
if fx != fy:
self.map[fx] = fy
C++程式碼如下:
class Solution {
public:
int removeStones(vector<vector<int>>& stones) {
if(stones.size() <= 1) return 0;
int N = stones.size();
vector<int> p(20000, -1);
for(auto stone : stones){
u(p, stone[0], stone[1] + 10000);
}
set<int> parents;
for(auto stone : stones){
parents.insert(f(p, stone[0]));
}
return N - parents.size();
}
private:
int f(vector<int> &p, int x){
if(p[x] == -1) return x;
return f(p, p[x]);
}
void u(vector<int> &p, int x, int y){
int px = f(p, x);
int py = f(p, y);
if(px != py){
p[px] = py;
}
}
};
日期
2018 年 11 月 24 日 —— 週日開始!一週就過去了~