[LeetCode] 947. Most Stones Removed with Same Row or Column 移除最多的同行或同列石頭



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. 1 <= stones.length <= 1000
  2. 0 <= stones[i][j] < 10000

這道題給了我們一堆石子坐標,說是每次可以移除一個石子,但是條件是必須有其他的一個石子跟這個石子共行或者共列,問最多可以移除多少個石子。從例子1來看,必須要按照一個特定的順序來移除石子,才能夠移除5個,若是隨機移除的話,可能會少於5個。而如果有兩個石子,各自分別不共行不共列,那么無論如何都無法移除石子,所以說只有屬於一個群組的石子才可以移除。這里定義的屬於同一的群組的方法是共行或共列,跟 Number of Islands 中的定義有些區別,那道題是所有相連的1當作一個島嶼,這里把相鄰的條件換成共行或共列即可。而對於屬於同一個群組的石子,總有辦法能按順序移除到只剩一個石子,所以總共有多少個群組,最終就會剩下多少個石子,最大的移除個數就是總石子個數減去群組個數。現在問題就變成了找不同群組的個數,博主最先想的利用和 Number of Islands 中一樣的方法,但不幸的是不論 DFS 還是 BFS 的方法都超時了,可能是由於不知道整個二維數組的范圍大小,所以記錄訪問過的結點只能保存橫縱坐標,不能 encode 成一個數字,從而超時了。為了能通過 OJ,必須要進行一些優化,由於這里關心的是同一行或者同一列上的結點,可以用兩個 HashMap,其中 rowMap 建立每個橫坐標和該行上所有出現的石子的縱坐標組成的數組之間的映射,同理,colMap 建立每個縱坐標和該列上所有出現的石子的橫坐標組成數組之間的映射。然后遍歷 rowMap,對於其中每個橫坐標調用遞歸函數,這個遞歸函數是計算群組中所有結點的個數,因為同一行上的所有結點必定都是屬於同一個群組的,所以可以一起計算。現在是為了找出跟該行上所有的石子共列的其他石子。通過 rowMap 可以找出所有和 row 共行的石子,對於每個石子,可以通過 colMap 找出跟其共列的石子,然后再次調用遞歸即可。找出了整個群組的個數,要移除的個數就是總個數減去1,將這個值加到結果 res 上。注意這里使用了個 trick,和0比較,取較大值。因為可能會遍歷到之前計算過的結點,此時遞歸函數直接返回了0,減去1后變為 -1,為了不減少 res 的值,要和0比較並取較大值,參見代碼如下:


解法一:

class Solution {
public:
    int removeStones(vector<vector<int>>& stones) {
        unordered_map<int, vector<int>> rowMap, colMap;
        unordered_set<int> rowVisited;
        int res = 0;
        for (auto stone : stones) {
            rowMap[stone[0]].push_back(stone[1]);
            colMap[stone[1]].push_back(stone[0]);
        }
        for (auto a : rowMap) {
            res += max(0, helper(rowMap, colMap, a.first, rowVisited) - 1);
        }
        return res;
    }
    int helper(unordered_map<int, vector<int>>& rowMap, unordered_map<int, vector<int>>& colMap, int row, unordered_set<int>& rowVisited) {
        if (rowVisited.count(row)) return 0;
        rowVisited.insert(row);
        int res = rowMap[row].size();
        for (int c : rowMap[row]) {
            for (int r : colMap[c]) {
                res += helper(rowMap, colMap, r, rowVisited);
            }
        }
        return res;
    }
};

對於這種求群組個數的題目,還有一大神器就是聯合查找 Union Find 的方法,在很多題目中都使用了,比如 Number of Islands II。一般來說,UF 算法的思路是每個個體先初始化為不同的群組,然后遍歷有關聯的兩個個體,如果發現其 getRoot 函數的返回值不同,則手動將二者加入一個群組,然后總群組數自減1。這里就要分別說一下 root 數組,和 getRoot 函數。兩個同群組的個體,通過 getRoot 函數一定會返回相同的值,但是其在 root 數組中的值不一定相同,可以類比成 getRoot 函數返回的是祖先,如果兩個人的祖先相同,那么其是屬於一個家族的(這里不是指人類共同的祖先哈)。root 可以用數組或者 HashMap 來表示,如果個體是數字的話,那么數組就 OK,如果個體是字符串的話,可能就需要用 HashMap 了。root 數組的初始化可以有兩種,可以均初始化為 -1,或者都初始化為不同的數字,博主一般喜歡初始化為不同的數字。getRoot 函數的寫法也可用遞歸或者迭代的方式,可參見博主之前的帖子 Redundant Connection II 中的討論部分。這里的話將每個石子都先當作是一個群組,這樣就初始化了n個群組,分別在 root 數組中進行初始化,然后遍歷任意兩個石子組合,若這兩個石子的橫縱坐標有一個相同的話,說明是同一個群組的,將二者關聯上,關聯之前要分別對其調用一次 getRoot 函數,以便找到其最終的祖先結點。更新結束了之后就要數最終還剩幾個群組了,將 root 數組遍歷一遍,當某個結點還是初始值時,說明其是一個群組的祖先結點,計數器 cnt 自增1,最終的結果就返回 n-cnt 即可,參見代碼如下:


解法二:

class Solution {
public:
    int removeStones(vector<vector<int>>& stones) {
        int cnt = 0, n = stones.size();
        vector<int> root(n);
        for (int i = 0; i < n; ++i) root[i] = i;
        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]) {
                    root[getRoot(root, i)] = getRoot(root, j);
                }
            }
        }
        for (int i = 0; i < n; ++i) {
            if (root[i] == i) ++cnt;
        }
        return n - cnt;
    }
    int getRoot(vector<int>& root, int x) {
        return x == root[x] ? x : getRoot(root, root[x]);
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/947


參考資料:

https://leetcode.com/problems/most-stones-removed-with-same-row-or-column/

https://leetcode.com/problems/most-stones-removed-with-same-row-or-column/discuss/197851/C%2B%2B-DFS

https://leetcode.com/problems/most-stones-removed-with-same-row-or-column/discuss/217790/C%2B%2B-union-find

https://leetcode.com/problems/most-stones-removed-with-same-row-or-column/discuss/197668/Count-the-Number-of-Islands-O(N)


LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM