[LeetCode] Minesweeper 掃雷游戲


 

Let's play the minesweeper game (Wikipediaonline game)!

You are given a 2D char matrix representing the game board. 'M' represents an unrevealed mine, 'E' represents an unrevealed empty square, 'B' represents a revealed blank square that has no adjacent (above, below, left, right, and all 4 diagonals) mines, digit ('1' to '8') represents how many mines are adjacent to this revealed square, and finally 'X' represents a revealed mine.

Now given the next click position (row and column indices) among all the unrevealed squares ('M' or 'E'), return the board after revealing this position according to the following rules:

 

  1. If a mine ('M') is revealed, then the game is over - change it to 'X'.
  2. If an empty square ('E') with no adjacent mines is revealed, then change it to revealed blank ('B') and all of its adjacent unrevealed squares should be revealed recursively.
  3. If an empty square ('E') with at least one adjacent mine is revealed, then change it to a digit ('1' to '8') representing the number of adjacent mines.
  4. Return the board when no more squares will be revealed.

Example 1:

Input: 

[['E', 'E', 'E', 'E', 'E'],
 ['E', 'E', 'M', 'E', 'E'],
 ['E', 'E', 'E', 'E', 'E'],
 ['E', 'E', 'E', 'E', 'E']]

Click : [3,0]

Output: 

[['B', '1', 'E', '1', 'B'],
 ['B', '1', 'M', '1', 'B'],
 ['B', '1', '1', '1', 'B'],
 ['B', 'B', 'B', 'B', 'B']]

Explanation:

 

Example 2:

Input: 

[['B', '1', 'E', '1', 'B'],
 ['B', '1', 'M', '1', 'B'],
 ['B', '1', '1', '1', 'B'],
 ['B', 'B', 'B', 'B', 'B']]

Click : [1,2]

Output: 

[['B', '1', 'E', '1', 'B'],
 ['B', '1', 'X', '1', 'B'],
 ['B', '1', '1', '1', 'B'],
 ['B', 'B', 'B', 'B', 'B']]

Explanation:

Note:

  1. The range of the input matrix's height and width is [1,50].
  2. The click position will only be an unrevealed square ('M' or 'E'), which also means the input board contains at least one clickable square.
  3. The input board won't be a stage when game is over (some mines have been revealed).
  4. For simplicity, not mentioned rules should be ignored in this problem. For example, you don't need to reveal all the unrevealed mines when the game is over, consider any cases that you will win the game or flag any squares.

 

這道題就是經典的掃雷游戲啦,經典到不能再經典,從Win98開始,附件中始終存在的游戲,和紙牌、紅心大戰、空當接龍一起稱為四大天王,曾經消耗了博主太多的時間。小時侯一直不太會玩掃雷,就是瞎點,完全不根據數字分析,每次點幾下就炸了,就覺得這個游戲好無聊。后來長大了一些,慢慢的理解了游戲的玩法,才發現這個游戲果然很經典,就像破解數學難題一樣,充滿了挑戰與樂趣。花樣百出的LeetCode這次把掃雷出成題,讓博主借機回憶了一把小時侯,不錯不錯,那么來做題吧。題目中圖文並茂,相信就算是沒玩過掃雷的也能弄懂了,而且規則也說的比較詳盡了,那么我們相對應的做法也就明了了。對於當前需要點擊的點,我們先判斷是不是雷,是的話直接標記X返回即可。如果不是的話,我們就數該點周圍的雷個數,如果周圍有雷,則當前點變為雷的個數並返回。如果沒有的話,我們再對周圍所有的點調用遞歸函數再點擊即可。參見代碼如下:

 

解法一:

class Solution {
public:
    vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {
        if (board.empty() || board[0].empty()) return {};
        int m = board.size(), n = board[0].size(), row = click[0], col = click[1], cnt = 0;
        if (board[row][col] == 'M') {
            board[row][col] = 'X';
        } else {
            for (int i = -1; i < 2; ++i) {
                for (int j = -1; j < 2; ++j) {
                    int x = row + i, y = col + j;
                    if (x < 0 || x >= m || y < 0 || y >= n) continue;
                    if (board[x][y] == 'M') ++cnt;
                }
            }
            if (cnt > 0) {
                board[row][col] = cnt + '0';
            } else {
                board[row][col] = 'B';
                for (int i = -1; i < 2; ++i) {
                    for (int j = -1; j < 2; ++j) {
                        int x = row + i, y = col + j;
                        if (x < 0 || x >= m || y < 0 || y >= n) continue;
                        if (board[x][y] == 'E') {
                            vector<int> nextPos{x, y};
                            updateBoard(board, nextPos);
                        }
                    }
                }
            }
        }
        return board;
    }
};

 

下面這種解法跟上面的解法思路基本一樣,寫法更簡潔了一些。可以看出上面的解法中的那兩個for循環出現了兩次,這樣顯得代碼比較冗余,一般來說對於重復代碼是要抽離成函數的,但那樣還要多加個函數,也麻煩。我們可以根據第一次找周圍雷個數的時候,若此時cnt個數為0並且標識是E的位置記錄下來,那么如果最后雷個數確實為0了的話,我們直接遍歷我們保存下來為E的位置調用遞歸函數即可,就不用再寫兩個for循環了,參見代碼如下:

 

解法二:

class Solution {
public:
    vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {
        if (board.empty() || board[0].empty()) return {};
        int m = board.size(), n = board[0].size(), row = click[0], col = click[1], cnt = 0;
        if (board[row][col] == 'M') {
            board[row][col] = 'X';
        } else {
            vector<vector<int>> neighbors;
            for (int i = -1; i < 2; ++i) {
                for (int j = -1; j < 2; ++j) {
                    int x = row + i, y = col + j;
                    if (x < 0 || x >= m || y < 0 || y >= n) continue;
                    if (board[x][y] == 'M') ++cnt;
                    else if (cnt == 0 && board[x][y] == 'E') neighbors.push_back({x, y});
                }
            }
            if (cnt > 0) {
                board[row][col] = cnt + '0';
            } else {
                for (auto a : neighbors) {
                    board[a[0]][a[1]] = 'B';
                    updateBoard(board, a);
                }
            }
        }
        return board;
    }
};

 

下面這種方法是上面方法的迭代寫法,用queue來存儲之后要遍歷的位置,這樣就不用遞歸調用函數了,參見代碼如下:

 

解法三:

class Solution {
public:
    vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {
        if (board.empty() || board[0].empty()) return {};
        int m = board.size(), n = board[0].size();
        queue<pair<int, int>> q({{click[0], click[1]}});
        while (!q.empty()) {
            int row = q.front().first, col = q.front().second, cnt = 0; q.pop();
            vector<pair<int, int>> neighbors;
            if (board[row][col] == 'M') board[row][col] = 'X';
            else {
                for (int i = -1; i < 2; ++i) {
                    for (int j = -1; j < 2; ++j) {
                        int x = row + i, y = col + j;
                        if (x < 0 || x >= m || y < 0 || y >= n) continue;
                        if (board[x][y] == 'M') ++cnt;
                        else if (cnt == 0 && board[x][y] == 'E') neighbors.push_back({x, y});
                    }
                }
            }
            if (cnt > 0) board[row][col] = cnt + '0';
            else {
                for (auto a : neighbors) {
                    board[a.first][a.second] = 'B';
                    q.push(a);
                }
            }
        }
        return board;
    }
};

 

參考資料:

https://discuss.leetcode.com/topic/80844/c-16-lines-bfs/2

https://discuss.leetcode.com/topic/80802/java-solution-dfs-bfs

 

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


免責聲明!

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



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