Leetcode 488 Zuma games

Keywords: Algorithm leetcode

You are participating in a variant of Zuma game.

In this Zuma game variant, there is a row of colored balls on the desktop. The color of each ball may be red 'R', yellow 'Y', blue 'B', green 'G' or white 'W'. You also have some colored balls in your hand.

Your goal is to empty all the balls on the table. Each round:

Choose any one of the colored balls in your hand and insert it into the volleyball on the table: between the two balls or at either end of the volleyball.
Then, if there are three or more balls of the same color connected, remove them.
If this removal operation also causes three or more balls of the same color to be connected, you can continue to remove these balls until the removal conditions are no longer met.
If all the balls on the table are removed, you are considered to have won the game.
Repeat this process until you win the game or have no more balls in your hand.
Give you a string board, which represents the volleyball at the beginning on the desktop. Another string hand is given to you to represent the colored ball in your hand. Please follow the above steps to remove all the balls on the table, calculate and return the minimum number of balls required. If you cannot remove all the balls on the table, return - 1.

Example 1:

Input: board = "WRRBBW", hand = "RB"
Output: - 1
Explanation: you cannot remove all balls from the desktop. The best situation available is:
-Insert an 'R' to change the desktop to wrrrbbw. WRRRBBW -> WBBW
-Insert a 'B' to make the desktop wbbbw. WBBBW -> WW
There are balls left on the table. No other balls can be inserted.

Example 2:

Input: board = "WWRRBBWW", hand = "WRBRW"
Output: 2
Explanation: to empty the ball on the desktop, follow the steps below:
-Insert an 'R' to change the desktop to wwrrrbbww. WWRRRBBWW -> WWBBWW
-Insert a 'B' to change the desktop to wwbbbww. WWBBBWW -> WWWW -> empty
Just take 2 balls out of your hand to empty the desktop.

class Solution {
    int ans, used;
    unordered_map<char, int> rem;
    stack<pair<char, int>> st;

    void dfs(int pos, string &board) {
        if (used >= ans)
            return;

        if (pos == board.size()) {
            if (st.empty())
                ans = used;
            return;
        }
        
        // Add the small ball in pos position
        if (!st.empty() && st.top().first == board[pos]) {
            st.top().second++;
        } else {
            st.emplace(board[pos], 1);
        }

        if (st.top().second >= 3) {
            if (pos + 1 == board.size() || board[pos + 1] != board[pos]) {
            // There is no ball of the same color behind, which can be directly eliminated
                auto tmp = st.top();    
                st.pop();
                dfs(pos + 1, board);
                st.push(tmp);
            } else {
            // For balls with the same color behind, you must insert small balls with different colors from the current position to separate them before you can eliminate the currently accumulated small balls; Otherwise, it must be eliminated with the same color ball behind.
                auto tmp = st.top(); // Maintenance site
                st.pop(); // Eliminate the currently accumulated small balls of the same color

                for (auto [ch, num] : rem) {
                    if (ch == board[pos])
                        continue;
                    for (int j = 1; j <= min(3, num); ++j) {
                        rem[ch] -= j;
                        used += j;

                        // Add j balls
                        if (!st.empty() && st.top().first == ch) {
                            st.top().second += j;
                        } else {
                            st.emplace(ch, j);
                        }

                        if (st.top().second >= 3) { // The inserted different color ball and the previous ball add up to three
                            auto tmp2 = st.top(); // Maintenance site
                            st.pop(); // Eliminate the same color ball
                            dfs(pos + 1, board);
                            st.push(tmp2); // Restore site
                        } else {
                            dfs(pos + 1, board);
                        }

                        // Restore site
                        if (st.top().second > j) {
                            st.top().second -= j;
                        } else {
                            st.pop();
                        }
                        used -= j;
                        rem[ch] += j;
                    }
                }

                st.push(tmp); // Restore site
            }
        }
        
        // Insert a small ball of the same color as the current position
        if (rem[board[pos]] >= 1 && (pos + 1 == board.size() || board[pos + 1] != board[pos])) {
            int lim = rem[board[pos]];
            for (int i = 1; i <= min(2, lim); ++i) {
                // Add i small balls of the same color
                rem[board[pos]] -= i;
                used += i;
                st.top().second += i;
                if (st.top().second >= 3) { // Accumulated three same color balls
                    auto tmp = st.top(); // Maintenance site
                    st.pop(); // Eliminate the same color ball
                    dfs(pos + 1, board);
                    st.push(tmp); // Restore site
                } else {
                    dfs(pos + 1, board);
                }

                // Restore site
                st.top().second -= i;
                used -= i;
                rem[board[pos]] += i;
            }
        }
        
        // The case where no extra ball is inserted
        // 1. There are less than three current color balls
        // 2. There are three small balls in the current color, but the current small ball is the same color as the next small ball, which indicates that the initial situation is XX...XX, and then the small ball in the middle is eliminated to form XXXX. This situation is allowed.
        if (st.top().second < 3 || (st.top().second == 3 && pos + 1 < board.size() && board[pos] == board[pos + 1]))
            dfs(pos + 1, board);

        // Restore site
        if (st.top().second == 1) {
            st.pop();
        } else {
            st.top().second--;
        }
    }
public:
    int findMinStep(string board, string hand) {
        for (char ch : hand)
            rem[ch]++;

        ans = 1e9;
        used = 0;
        dfs(0, board);
        return ans == 1e9 ? -1 : ans;
    }
};

Posted by wisewood on Tue, 09 Nov 2021 14:21:41 -0800