LeetCode 289. Game of Life

题目

According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970."

Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):

  1. Any live cell with fewer than two live neighbors dies, as if caused by under-population.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by over-population..
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

Write a function to compute the next state (after one update) of the board given its current state. The next state is created by applying the above rules simultaneously to every cell in the current state, where births and deaths occur simultaneously.

Example:

Input: 
[
  [0,1,0],
  [0,0,1],
  [1,1,1],
  [0,0,0]
]
Output: 
[
  [0,0,0],
  [1,0,1],
  [0,1,1],
  [0,1,0]
]

Follow up:

  1. Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells first and then use their updated values to update other cells.
  2. In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause problems when the active area encroaches the border of the array. How would you address these problems?

这道题是非常经典的题,当年数据结构课本第一章就是这个,记忆犹新。它给定了一个grid,每个格子0为dead、1为alive,有一套规则规定了每个格子会根据当前状态的邻居(上下左右和对角线,共8个)的dead/alive状态切换到下一个状态,要求进行k次状态切换后的格子内容(LC这题是1次)。

其实做起来很简单,只需要对所有格子进行遍历,然后对这些格子的所有邻居进行遍历,然后计算alive的邻居的数量,再根据规则进行update就可以了。需要注意的地方就是大的双重循环套小的双重循环(-1到1),if的判断条件注意不要写错,以及注意不能直接在原始grid上改,不然计算邻居的时候就会算成更新后的状态。代码时间复杂度O(M*N),空间复杂度O(M*N),运行时间4ms,61.95%,空间8.6M,100%:

class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        vector<vector<int>> board_copy(board);
        int rows = board.size();
        int cols = board[0].size();
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                int count = 0;
                for (int m = -1; m < 2; m++) {
                    for (int n = -1; n < 2; n++) {
                        if ((0 <= i + m && i + m < rows) && (0 <= j + n && j + n < cols) && !(m == 0 && n == 0)) {
                            if ((board_copy[i + m][j + n] == 1)) {
                                count++;
                            }
                        }
                    }
                }
                
                if (board_copy[i][j] == 1 && (count < 2 || count > 3)) {
                    board[i][j] = 0;
                }
                else if (board_copy[i][j] == 0 && count == 3) {
                    board[i][j] = 1;
                }
            }
        }
    }
};

题目的一个followup提出,如果要求O(1) space做出来,怎么做。看了solution才知道可以把从dead->alive和alive->dead的格子标记为非0或1的数字,比如dead->alive标为2,alive->dead标为-1,每次check邻居的死活的时候只需要看格子的绝对值是否为1即可,最后在遍历完整个数组以后,把所有的2变成1,-1变成0。代码如下,时间复杂度O(M*N)不变,运行0ms,100%,空间变成了O(1),8.7M,85.17%……看起来咋优化了空间以后空间反倒变多了,时间倒是提升了:

class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        int rows = board.size();
        int cols = board[0].size();
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                int count = 0;
                for (int m = -1; m < 2; m++) {
                    for (int n = -1; n < 2; n++) {
                        if ((0 <= i + m && i + m < rows) && (0 <= j + n && j + n < cols) && !(m == 0 && n == 0)) {
                            if ((abs(board[i + m][j + n]) == 1)) {
                                count++;
                            }
                        }
                    }
                }
                
                if (board[i][j] == 1 && (count < 2 || count > 3)) {
                    board[i][j] = -1;  // live->dead
                }
                else if (board[i][j] == 0 && count == 3) {
                    board[i][j] = 2;  // dead->live
                }
            }
        }
        
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] > 0) {
                    board[i][j] = 1;
                }
                else {
                    board[i][j] = 0;
                }
            }
        }
    }
};

另一个followup是说,如果这个格子有无限大,活的格子很少,怎么办。这个现在先暂时跳过吧……大概意思我能get到就是遍历所有活着的格子的邻居,为这些下次可能是活的格子进行计数并判断它们的下个状态。贴个链接下次方便找:https://leetcode.com/problems/game-of-life/discuss/73217/Infinite-board-solution

然后某公司的OA也是这道题的derivation,但是要求的是求k次变换以后的grid,并且更改状态的规则是作为输入给出的,告诉你每个格子如果有i个邻居是活的那么这个格子的下一个状态是什么。

代码:

#include <bits/stdc++.h>

using namespace std;

string ltrim(const string &);
string rtrim(const string &);
vector<string> split(const string &);


/*
 * Complete the 'gridGame' function below.
 *
 * The function is expected to return a 2D_INTEGER_ARRAY.
 * The function accepts following parameters:
 *  1. 2D_INTEGER_ARRAY grid
 *  2. INTEGER k
 *  3. STRING_ARRAY rules
 */

vector<vector<int>> gridGame(vector<vector<int>> grid, int k, vector<string> rules) {
    vector<vector<int>> grid_modified(grid);
    for (int round = 0; round < k; round++) {
        int rows = grid.size();
        int cols = grid[0].size();

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                int count = 0;
                for (int m = -1; m < 2; m++) {
                    for (int n = -1; n < 2; n++) {
                        if ((0 <= m + i && m + i < rows) && (0 <= n + j && n + j < cols) && !(m == 0 && n == 0)) {
                            if (grid[m + i][n + j] == 1) {
                                count++;
                            }
                        }
                    }
                }

                grid_modified[i][j] = rules[count] == "alive" ? 1 : 0;
            }
        }
        grid = grid_modified;
    }
    return grid_modified;
}
int main()
{
    ofstream fout(getenv("OUTPUT_PATH"));

    string grid_rows_temp;
    getline(cin, grid_rows_temp);

    int grid_rows = stoi(ltrim(rtrim(grid_rows_temp)));

    string grid_columns_temp;
    getline(cin, grid_columns_temp);

    int grid_columns = stoi(ltrim(rtrim(grid_columns_temp)));

    vector<vector<int>> grid(grid_rows);

    for (int i = 0; i < grid_rows; i++) {
        grid[i].resize(grid_columns);

        string grid_row_temp_temp;
        getline(cin, grid_row_temp_temp);

        vector<string> grid_row_temp = split(rtrim(grid_row_temp_temp));

        for (int j = 0; j < grid_columns; j++) {
            int grid_row_item = stoi(grid_row_temp[j]);

            grid[i][j] = grid_row_item;
        }
    }

    string k_temp;
    getline(cin, k_temp);

    int k = stoi(ltrim(rtrim(k_temp)));

    string rules_count_temp;
    getline(cin, rules_count_temp);

    int rules_count = stoi(ltrim(rtrim(rules_count_temp)));

    vector<string> rules(rules_count);

    for (int i = 0; i < rules_count; i++) {
        string rules_item;
        getline(cin, rules_item);

        rules[i] = rules_item;
    }

    vector<vector<int>> result = gridGame(grid, k, rules);

    for (int i = 0; i < result.size(); i++) {
        for (int j = 0; j < result[i].size(); j++) {
            fout << result[i][j];

            if (j != result[i].size() - 1) {
                fout << " ";
            }
        }

        if (i != result.size() - 1) {
            fout << "\n";
        }
    }

    fout << "\n";

    fout.close();

    return 0;
}

string ltrim(const string &str) {
    string s(str);

    s.erase(
        s.begin(),
        find_if(s.begin(), s.end(), not1(ptr_fun<int, int>(isspace)))
    );

    return s;
}

string rtrim(const string &str) {
    string s(str);

    s.erase(
        find_if(s.rbegin(), s.rend(), not1(ptr_fun<int, int>(isspace))).base(),
        s.end()
    );

    return s;
}

vector<string> split(const string &str) {
    vector<string> tokens;

    string::size_type start = 0;
    string::size_type end = 0;

    while ((end = str.find(" ", start)) != string::npos) {
        tokens.push_back(str.substr(start, end - start));

        start = end + 1;
    }

    tokens.push_back(str.substr(start));

    return tokens;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值