根据百度百科,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞具有一个初始状态 live(1)即为活细胞, 或 dead(0)即为死细胞。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
- 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
- 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
- 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
- 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
根据当前状态,写一个函数来计算面板上细胞的下一个(一次更新后的)状态。下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。
示例:
输入: [ [0,1,0], [0,0,1], [1,1,1], [0,0,0] ] 输出: [ [0,0,0], [1,0,1], [0,1,1], [0,1,0] ]
进阶:
- 你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
- 本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?
class Solution
{
public:
void gameOfLife(vector<vector<int>> &board)
{
int row = board.size();
int col = board[0].size();
vector<vector<int>> ans(row, vector<int>(col, 0));
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
int cnt = FindLive(board, i, j, row, col);
if (cnt < 2 && board[i][j] == 1)
{
ans[i][j] = 0;
}
else if (cnt > 3 && board[i][j] == 1)
{
ans[i][j] = 0;
}
else if (cnt == 3 && board[i][j] == 0)
{
ans[i][j] = 1;
}
else
{
ans[i][j] = board[i][j];
}
}
}
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
board[i][j] = ans[i][j];
}
}
}
int FindLive(vector<vector<int>> &board, int x, int y, int row, int col)
{
int cnt = 0;
int dx[] = { 0, 0, 1, 1, 1, -1, -1, -1 };
int dy[] = { 1, -1, 0, 1, -1, 0, 1, -1 };
for (int i = 0; i < 8; i++)
{
int newX = x + dx[i];
int newY = y + dy[i];
if (newX < 0 || newX >= row || newY < 0 || newY >= col)
continue;
if (board[newX][newY] == 1)
cnt++;
}
return cnt;
}
};
原地算法参考
统计八个方位的存活细胞数目(原地更新):
- 死细胞,周围存活细胞数=3,复活dead->live。
- 活细胞,周围存活细胞数<2 or >3,死掉live->dead。
但是原地更新,肯定不能用0和1更新board[][],因为会破会下一次循环计算。
- 可以使用3代表0
- 可以使用2代表1
所以,board[][]中1和2都是live,0和3都是dead。那么,每次最末尾的时候都需要一次解码过程,也就是将3->1和2->0
void gameOfLife(vector<vector<int>>& board) {
int m = board.size(), n = board[0].size();
for(int i = 0; i < m; ++i){
for(int j = 0; j < n; ++j){
int lives = 0;
if(i > 0){//判断上边
lives += board[i-1][j] == 1 || board[i-1][j] == 2 ? 1:0;
}
if(i > 0 && j < n - 1){//判断右上角
lives += board[i-1][j+1] == 1 || board[i-1][j+1] == 2 ? 1:0;
}
if(j < n - 1){//判断右边
lives += board[i][j+1] == 1 || board[i][j+1] == 2 ? 1:0;
}
if(i < m - 1 && j < n - 1){//判断右下角
lives += board[i+1][j+1] == 1 || board[i+1][j+1] == 2 ? 1:0;
}
if(i < m - 1){//判断下边
lives += board[i+1][j] == 1 || board[i+1][j] == 2 ? 1:0;
}
if(i < m - 1 && j > 0){//判断左下角
lives += board[i+1][j-1] == 1 || board[i+1][j-1] == 2 ? 1:0;
}
if(j > 0){//判断左边
lives += board[i][j-1] == 1 || board[i][j-1] == 2 ? 1:0;
}
if(i > 0 && j > 0){//判断左上角
lives += board[i-1][j-1] == 1 || board[i-1][j-1] == 2 ? 1:0;
}
if(board[i][j] == 0 && lives == 3){// 更新
board[i][j] = 3;//dead->live
} else if(board[i][j] == 1){
if(lives < 2 || lives > 3){
board[i][j] = 2;//live->dead
}
}
}
}
for(int i = 0; i < m; ++i){// 解码
for(int j = 0; j < n; ++j){
board[i][j] = board[i][j] % 2;
}
}
}