双指针——粉碎糖果(leetcode 723)

题目描述

这个问题是实现一个简单的消除算法。

给定一个二维整数数组 board 代表糖果所在的方格,不同的正整数 board[i][j] 代表不同种类的糖果,如果 board[i][j] = 0 代表 (i, j) 这个位置是空的。给定的方格是玩家移动后的游戏状态,现在需要你根据以下规则粉碎糖果,使得整个方格处于稳定状态并最终输出。

    如果有三个及以上水平或者垂直相连的同种糖果,同一时间将它们粉碎,即将这些位置变成空的。
    在同时粉碎掉这些糖果之后,如果有一个空的位置上方还有糖果,那么上方的糖果就会下落直到碰到下方的糖果或者底部,这些糖果都是同时下落,也不会有新的糖果从顶部出现并落下来。
    通过前两步的操作,可能又会出现可以粉碎的糖果,请继续重复前面的操作。
    当不存在可以粉碎的糖果,也就是状态稳定之后,请输出最终的状态。

你需要模拟上述规则并使整个方格达到稳定状态,并输出。

 

样例 :

输入:
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]]

输出:
[[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]]

 

注释 :

    board 数组的行数区间是 [3, 50]。
    board[i] 数组的长度区间(即 board 数组的列数区间)是 [3, 50]。
    每个 board[i][j] 初始整数范围是 [1, 2000]。

算法分析

方法:特殊用途的网络 Ad-Hoc [Accepted]

我们需要模拟执行所描述的算法。它由两个主要步骤组成:粉碎糖果和掉落糖果。我们每个步骤都是单独完成的。

算法:

    粉碎糖果

我们如果是按行或者列进行粉碎,那么在粉碎的过程中可能会粉碎到其他行或列的需要粉碎糖果的一部分。如果 board 是:

123
145
111

那么我们如果先粉碎了第一列的 1 糖果,那么就会影响第三行 1 糖果的粉碎。

为了解决这个问题,我们可以先把要粉碎的糖果做个标记。我们可以使用 toCrush 布尔数组来辅助,或者我们可以将糖果标记为相反数(例如,board[i][j] = -Math.abs(board[i][j]))。

扫描 board 有两个方法。让我们把 line 称为 board 的行或列。

对于每一 line,我们可以使用一个滑动窗口(或者 Python 中的 itertools.groupby)来查找相同字符的连续段。如果段的长度大于 3,那么就应该标记它。

或者,对于每一 line,我们可以查看每一 line 的 width-3 段(一段为 3 个):如果它们都相同,那么我们应该标记这 3 个。

之后,将有标记的位置设置为 0 来粉碎糖果。

    掉落糖果

对于每一列,我们希望所有的糖果都放在底部。一种方法是顺序遍历列中为粉碎的糖果放到栈中,然后以相反的顺序遍历列且弹出栈元素设置糖果。

或者,我们可以使用滑动窗口方法,read 指针读元素,write 指针写元素。指针以逆序遍历列元素,当 read 指针遇到糖果时,write 指针将它写下来并移动到下一个位置。然后,write 指针将向列的其余部分写入零。

代码

class Solution {
public:
    vector<vector<int>> candyCrush(vector<vector<int>>& board) {

        int row = board.size();
        int col = board[0].size();
        bool todo = false;

        // 遍历行
        for(int r = 0; r < row; ++r) {
            for(int c = 0; c < col - 2; ++c) {
                int v = abs(board[r][c]);
                if(v != 0 && v == abs(board[r][c+1]) && v == abs(board[r][c+2])) {
                    board[r][c] = board[r][c+1] = board[r][c+2] = -v;
                    todo = true;
                }
            }
        }

        // 遍历列
        for(int c = 0; c < col; ++c) {
            for(int r = 0; r < row - 2; ++r) {
                int v = abs(board[r][c]);
                if(v != 0 && v == abs(board[r+1][c]) && v == abs(board[r+2][c])) {
                    board[r][c] = board[r+1][c] = board[r+2][c] = -v;
                    todo = true;
                }
            }
        }

        if(todo) {
            for(int c = 0; c < col; ++c) {
                int row_down = row - 1;
                for(int r = row -1; r >= 0; --r) {
                    if(board[r][c] > 0) {
                        board[row_down][c] = board[r][c];
                        --row_down;
                    }
                }
                while(row_down >= 0) {
                    board[row_down][c] = 0;
                    --row_down;
                }
            }
        }
        return todo ? candyCrush(board) : board;
    }
};

时间复杂度分析

时间复杂度:O((RC)^2)。其中 R,C 指的是 board 的行和列。
空间复杂度:O(1),因为在 board 进行原地修改。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值