[LeetCode] 723. Candy Crush 糖果消消樂


 

This question is about implementing a basic elimination algorithm for Candy Crush.

Given a 2D integer array board representing the grid of candy, different positive integers board[i][j] represent different types of candies. A value of board[i][j] = 0 represents that the cell at position (i, j) is empty. The given board represents the state of the game following the player's move. Now, you need to restore the board to a stable state by crushing candies according to the following rules:

  1. If three or more candies of the same type are adjacent vertically or horizontally, "crush" them all at the same time - these positions become empty.
  2. After crushing all candies simultaneously, if an empty space on the board has candies on top of itself, then these candies will drop until they hit a candy or bottom at the same time. (No new candies will drop outside the top boundary.)
  3. After the above steps, there may exist more candies that can be crushed. If so, you need to repeat the above steps.
  4. If there does not exist more candies that can be crushed (ie. the board is stable), then return the current board.

You need to perform the above rules until the board becomes stable, then return the current board.

Example 1:

Input:
board = 
[[110,5,112,113,114],[210,211,5,213,214],[310,311,3,313,314],[410,411,412,5,414],[5,1,512,3,3],[610,4,1,613,614],[710,1,2,713,714],[810,1,2,1,1],[1,1,2,2,2],[4,1,4,4,1014]]
Output:
[[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[110,0,0,0,114],[210,0,0,0,214],[310,0,0,113,314],[410,0,0,213,414],[610,211,112,313,614],[710,311,412,613,714],[810,411,512,713,1014]]
Explanation: 

 

Note:

  1. The length of board will be in the range [3, 50].
  2. The length of board[i] will be in the range [3, 50].
  3. Each board[i][j] will initially start as an integer in the range [1, 2000].

 

這道題就是糖果消消樂,博主剛開始做的時候,沒有看清楚題意,以為就像游戲中的那樣,每次只能點擊一個地方,然后消除后糖果落下,這樣會導致一個問題,就是原本其他可以消除的地方在糖果落下后可能就沒有了,所以博主在想點擊的順序肯定會影響最終的 stable 的狀態,可是題目怎么沒有要求返回所剩糖果最少的狀態?后來發現,其實這道題一次消除 table 中所有可消除的糖果,然后才下落,形成新的 table,這樣消除后得到的結果就是統一的了,這樣也大大的降低了難度。下面就來看如何找到要消除的糖果,可能有人會覺得像之前的島嶼的題目一樣找連通區域,可是這道題的有限制條件,只有橫向或豎向相同的糖果數達到三個才能消除,並不是所有的連通區域都能消除,所以找連通區域不是一個好辦法。最好的辦法其實是每個糖果單獨檢查其是否能被消除,然后把所有能被刪除的糖果都標記出來統一刪除,然后在下落糖果,然后再次查找,直到無法找出能夠消除的糖果時達到穩定狀態。好,用一個數組來保存可以被消除的糖果的位置坐標,判斷某個位置上的糖果能否被消除的方法就是檢查其橫向和縱向的最大相同糖果的個數,只要有一個方向達到三個了,當前糖果就可以被消除。所以對當前糖果的上下左右四個方向進行查看,用四個變量 x0, x1, y0, y1,其中 x0 表示上方相同的糖果的最大位置,x1 表示下方相同糖果的最大位置,y0 表示左邊相同糖果的最大位置,y1 表示右邊相同糖果的最大位置,均初始化為當前糖果的位置,然后使用 while 循環向每個方向遍歷,注意並不需要遍歷到盡頭,而是只要遍歷三個糖果就行了,因為一旦查到了三個相同的,就說明當前的糖果已經可以消除了,沒必要再往下查了。查的過程還要注意處理越界情況,好,得到了上下左右的最大的位置,分別讓相同方向的做差,如果水平和豎直方向任意一個大於3了,就說明可以消除,將坐標加入數組 del 中。注意這里一定要大於3,是因為當發現不相等退出 while 循環時,坐標值已經改變了,所以已經多加了或者減了一個,所以差值要大於3。遍歷完成后,如果數組 del 為空,說明已經 stable 了,直接 break 掉,否則將要消除的糖果位置都標記為0,然后進行下落處理。下落處理實際上是把數組中的0都移動到開頭,那么就從數組的末尾開始遍歷,用一個變量t先指向末尾,然后然后當遇到非0的數,就將其和t位置上的數置換,然后t自減1,這樣t一路減下來都是非0的數,而0都被置換到數組開頭了,參見代碼如下:

 

class Solution {
public:
    vector<vector<int>> candyCrush(vector<vector<int>>& board) {
        int m = board.size(), n = board[0].size();
        while (true) {
            vector<pair<int, int>> del;
            for (int i = 0; i < m; ++i) {
                for (int j = 0; j < n; ++j) {
                    if (board[i][j] == 0) continue;
                    int x0 = i, x1 = i, y0 = j, y1 = j;
                    while (x0 >= 0 && x0 > i - 3 && board[x0][j] == board[i][j]) --x0;
                    while (x1 < m && x1 < i + 3 && board[x1][j] == board[i][j]) ++x1;
                    while (y0 >= 0 && y0 > j - 3 && board[i][y0] == board[i][j]) --y0;
                    while (y1 < n && y1 < j + 3 && board[i][y1] == board[i][j]) ++y1;
                    if (x1 - x0 > 3 || y1 - y0 > 3) del.push_back({i, j});
                }
            }
            if (del.empty()) break;
            for (auto a : del) board[a.first][a.second] = 0;
            for (int j = 0; j < n; ++j) {
                int t = m - 1;
                for (int i = m - 1; i >= 0; --i) {
                    if (board[i][j]) swap(board[t--][j], board[i][j]);   
                }
            }
        }
        return board;
    }
};

 

Github 同步地址:

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

 

參考資料:

https://leetcode.com/problems/candy-crush/

https://leetcode.com/problems/candy-crush/discuss/178366/Another-Java-Solution

https://leetcode.com/problems/candy-crush/discuss/109225/Simple-C%2B%2B-brute-force

https://leetcode.com/problems/candy-crush/discuss/113914/15-ms-Short-Java-Solution-Mark-crush-with-negative-value

 

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


免責聲明!

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



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