【Leetcode】723. Candy Crush

本文解析了LeetCode上糖果消消乐问题的算法实现,介绍了一种通过标记要消除的糖果并更新矩阵的方法。该算法每轮找出所有待消除元素,完成消除后再将上方元素下移填充空白。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目地址:

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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值