稀碎从零算法笔记Day33-LeetCode:生命游戏

今天是“耶稣受难人”,笔者给自己放了天假hhh

且慢,还是会写一点的。

根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。 

题型:矩阵、原地实现、位运算

链接:289. 生命游戏 - 力扣(LeetCode)

来源:LeetCode

题目描述

给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:

规则才是重点!

  1. 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
  2. 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
  3. 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
  4. 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;

下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。

请配合样例食用

题目样例

示例 1:

输入:board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
输出:[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]

示例 2:

输入:board = [[1,1],[1,0]]
输出:[[1,1],[1,1]]

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 25
  • board[i][j] 为 0 或 1(非常人性化的矩阵设计,给了许多可能)

进阶:

  • 你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
  • 本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?

题目思路

一个理解起来不难,但可能实现起来比较麻烦的矩阵模拟题。

抛开要求的【原地】不谈外,用一个新的二维数组辅助,在遍历也感觉很麻烦,外加空间复杂度直接干到了O( row * column)

那么可以考虑下题目要求的【原地】——即O(1)怎么实现。

笔者愚笨,在看了题解+评论区 2h后,才明白这套【原地+二进制表示法】。

这边先说一下,二进制(笔者只写0-3,所以2位足够)中:0是00,1是01,2是10,3是11 。
先说一下表示方法:用0表示【死细胞】状态,1表示【活细胞】状态。涉及两位数,那么【01】就是【由活向死】,【10】就是【由死向活】。那么如何实现这个状态的转化呢?这边就要用到二进制的【右移】操作(推荐自己在纸上写一下)。描述的话:假设数字的右边是个悬崖,有碾压墙把数字右推。数字右移1位,那么最右边那个就‘消失’了,【10】也就变成了【01】。

说完这个二进制表示状态的思路后,剩下的就是【原地】。这边原地笔者是先遍历【第一行】和【第一列】,确定他们会不会转化为【全0】(后面单独处理就好)。之后抛开这一行一列,遍历剩下的矩阵,按照规则找到每个点的【活细胞数】,进而确定状态。

最后通过右移操作,将矩阵更新为新的【0-1矩阵】

C++代码

class Solution {
    int rowLen,columnLen;
public:   
    void gameOfLife(vector<vector<int>>& board) {
        // 评论区双位表示细胞状态
        // 00  : 第一位表示未来 ,第二位表示现在。现在 -> 未来 右移一位。
        rowLen = board.size();
        columnLen = board[0].size();
        int life;
        for(int i=0;i<rowLen;i++)
        {
            for(int j=0;j<columnLen;j++)
            {
                life = getLife(i,j,board);//获取周围有几个活细胞
                // cout<<life<<endl;
                // 当前是活细胞
                if(board[i][j] == 1)
                {
                    if(life == 2 || life == 3)
                        board[i][j] += 2;
                // 否则未来就会死,01,保持不变
                }
                else
                {
                //死细胞从 00 变成 10 
                    if(life == 3)
                        board[i][j] += 2;
                }
            }
        }
        for(auto &answer : board)//用引用,来改变原数组的值
            for(int &ans : answer )
                {
                    // 全都右移1位
                    ans = ans>>1;
                }
    }
        int getLife(int row, int column,vector<vector<int>> numpy)
    {
        int life = 0;
        //不能有{0,0} 因为 board[row][column]相当于当前元素
        vector<vector<int>> lifeNumpy = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};//8个状态数组,为了求出周围8个细胞的状态
        for(int i = 0;i < 8;i++)
        {
            int x = row - lifeNumpy[i][0];
            int y = column - lifeNumpy[i][1];
            if(x<0 || x>=rowLen || y<0 || y >= columnLen)//矩阵外的细胞不处理
                continue;
            //因为board的元素除了0就是1,相当于统计周围1的个数
            life += numpy[x][y]&1;//应该只看最末一位,因为board不断变,状态只看原来的
        }
        return life;
    }     
};

结算页面

  • 19
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值