题目地址:
https://leetcode.com/problems/candy-crush/description/
给定一个 m × n m\times n m×n的矩阵 A A A,题目保证 ∀ i , j , A [ i ] [ j ] > 0 \forall i, j,A[i][j]>0 ∀i,j,A[i][j]>0。这是糖果消消乐的局面,每一轮要进行如下操作:如果某个位置的左右延伸长度 3 3 3的段都相等,或者其上下延伸长 3 3 3的段都相等,那么组成长度为 3 3 3的段就会同时都被消掉。每一轮找到所有要被消掉的数之后,将上面的数向下填充,上面空出的位置填 0 0 0。问经过若干轮直到无法再消去新的块的时候,矩阵 A A A变成什么样。
每一轮开始先求出所有要被消去的位置,存起来,然后将其消去即可。找位置的时候,只需要判断其上下是否都与其相等,或者其左右是否都与其相等。消去的时候,可以遍历每一列,然后进行整体的消去。代码如下:
class Solution {
public:
using PII = pair<int, int>;
#define x first
#define y second
vector<vector<int>> candyCrush(vector<vector<int>>& a) {
int m = a.size(), n = a[0].size();
auto phash = [](const PII& p) {
auto h = hash<int>();
return h(p.x) ^ h(p.y);
};
auto mark = [&] {
unordered_set<PII, decltype(phash)> st(0, phash);
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++) {
if (!a[i][j]) continue;
int x = a[i][j];
if (i - 1 >= 0 && a[i - 1][j] == x && i + 1 < m && a[i + 1][j] == x) {
st.insert({i, j});
st.insert({i - 1, j});
st.insert({i + 1, j});
}
if (j - 1 >= 0 && a[i][j - 1] == x && j + 1 < n && a[i][j + 1] == x) {
st.insert({i, j});
st.insert({i, j - 1});
st.insert({i, j + 1});
}
}
return st;
};
auto crush = [&](unordered_set<PII, decltype(phash)>& st) {
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++) {
if (!a[i][j] || !st.count({i, j})) continue;
int k = i;
st.erase({i, j});
while (k + 1 < m && st.count({k + 1, j})) {
st.erase({k + 1, j});
k++;
}
int k1 = i - 1;
while (k1 >= 0) a[k--][j] = a[k1--][j];
while (k >= 0 && a[k][j]) a[k--][j] = 0;
}
};
while (true) {
auto st = mark();
if (st.empty()) break;
crush(st);
}
return a;
}
};
时间复杂度 O ( k m n ) O(kmn) O(kmn)( k k k是消去的轮数,取决于具体数据),空间 O ( m n ) O(mn) O(mn)。