488_祖玛游戏_2021.11.9
你正在参与祖玛游戏的一个变种。
在这个祖玛游戏变体中,桌面上有 一排 彩球,每个球的颜色可能是:红色 'R'、黄色 'Y'、蓝色 'B'、绿色 'G' 或白色 'W' 。你的手中也有一些彩球。
你的目标是 清空 桌面上所有的球。每一回合:
从你手上的彩球中选出 任意一颗 ,然后将其插入桌面上那一排球中:两球之间或这一排球的任一端。
接着,如果有出现 三个或者三个以上 且 颜色相同 的球相连的话,就把它们移除掉。
如果这种移除操作同样导致出现三个或者三个以上且颜色相同的球相连,则可以继续移除这些球,直到不再满足移除条件。
如果桌面上所有球都被移除,则认为你赢得本场游戏。
重复这个过程,直到你赢了游戏或者手中没有更多的球。
给你一个字符串 board ,表示桌面上最开始的那排球。另给你一个字符串 hand ,表示手里的彩球。请你按上述操作步骤移除掉桌上所有球,计算并返回所需的 最少 球数。如果不能移除桌上所有的球,返回 -1 。
DFS求解过程:
1 class Solution { 2 public: 3 int ans, used; 4 unordered_map<char, int> rem; //记录hand中各彩球个数的字典 5 stack<pair<char, int>> st; //记录board中各彩球的个数的栈 6 7 void dfs(int pos, string &board) { 8 // 判断球是否用尽 9 if (used >= ans) 10 return; 11 12 // 判断桌上球的遍历位置是否结束 13 if (pos == board.size()) { 14 // 判断 15 if (st.empty()) 16 ans = used; 17 return; 18 } 19 20 // 将pos位置的小球加入 21 if (!st.empty() && st.top().first == board[pos]) { 22 st.top().second++; 23 } 24 else { 25 st.emplace(board[pos], 1); 26 } 27 28 if (st.top().second >= 3) { 29 if (pos + 1 == board.size() || board[pos + 1] != board[pos]) { 30 // 后面没有相同颜色的球,可以直接消去 31 auto tmp = st.top(); 32 st.pop(); 33 dfs(pos + 1, board); 34 st.push(tmp); 35 } 36 else { 37 // 后面有相同颜色的球,必须插入与当前位置不同色的小球进行分隔后,才能将当前积累的小球消去;否则必须和后面的同色球一起消去。 38 auto tmp = st.top(); // 维护现场 39 st.pop(); // 消去当前积累的同色小球 40 41 for (auto [ch, num] : rem) { 42 if (ch == board[pos]) 43 continue; 44 for (int j = 1; j <= min(3, num); ++j) { 45 rem[ch] -= j; 46 used += j; 47 48 // 加入j个小球 49 if (!st.empty() && st.top().first == ch) { 50 st.top().second += j; 51 } 52 else { 53 st.emplace(ch, j); 54 } 55 56 if (st.top().second >= 3) { // 插入的异色球和之前的球累加达到了三个 57 auto tmp2 = st.top(); // 维护现场 58 st.pop(); // 消去同色球 59 dfs(pos + 1, board); 60 st.push(tmp2); // 还原现场 61 } 62 else { 63 dfs(pos + 1, board); 64 } 65 66 // 还原现场 67 if (st.top().second > j) { 68 st.top().second -= j; 69 } 70 else { 71 st.pop(); 72 } 73 used -= j; 74 rem[ch] += j; 75 } 76 } 77 78 st.push(tmp); // 还原现场 79 } 80 } 81 82 // 插入与当前位置同色的小球 83 if (rem[board[pos]] >= 1 && (pos + 1 == board.size() || board[pos + 1] != board[pos])) { 84 int lim = rem[board[pos]]; 85 for (int i = 1; i <= min(2, lim); ++i) { 86 // 加入i个同色小球 87 rem[board[pos]] -= i; 88 used += i; 89 st.top().second += i; 90 if (st.top().second >= 3) { // 累积同色球达到三个 91 auto tmp = st.top(); // 维护现场 92 st.pop(); // 消去同色球 93 dfs(pos + 1, board); 94 st.push(tmp); // 还原现场 95 } 96 else { 97 dfs(pos + 1, board); 98 } 99 100 // 还原现场 101 st.top().second -= i; 102 used -= i; 103 rem[board[pos]] += i; 104 } 105 } 106 107 // 不额外插入小球的情形 108 // 1. 当前颜色小球不满三个 109 // 2. 当前颜色小球满三个,但当前小球和下一小球同色,这说明初始情形为XX...XX,之后中间的小球被消去,从而形成了XXXX,这种情况是允许的。 110 if (st.top().second < 3 || (st.top().second == 3 && pos + 1 < board.size() && board[pos] == board[pos + 1])) 111 dfs(pos + 1, board); 112 113 // 还原现场 114 if (st.top().second == 1) { 115 st.pop(); 116 } 117 else { 118 st.top().second--; 119 } 120 } 121 public: 122 int findMinStep(string board, string hand) { 123 for (char ch : hand) 124 rem[ch]++; 125 126 ans = 1e9; //定义无穷大,实际作用是记录最终手上的球全部用完 127 used = 0; //用过的球 128 129 // 广度优先边路 130 dfs(0, board); 131 132 133 return ans == 1e9 ? -1 : ans; 134 } 135 }; 136 137 138 int main(void) 139 { 140 string board = "WWRRBBWW"; 141 string hand = "WRBRW"; 142 143 Solution *s = new Solution(); 144 cout << s->findMinStep(board, hand) << endl; 145 146 delete(s); 147 148 getchar(); 149 return 0; 150 }