1. 程式人生 > >Leetcode:289.生命遊戲

Leetcode:289.生命遊戲

根據百度百科,生命遊戲,簡稱為生命,是英國數學家約翰·何頓·康威在1970年發明的細胞自動機。

給定一個包含 m × n 個格子的面板,每一個格子都可以看成是一個細胞。每個細胞具有一個初始狀態 live(1)即為活細胞, 或 dead(0)即為死細胞。每個細胞與其八個相鄰位置(水平,垂直,對角線)的細胞都遵循以下四條生存定律:

  1. 如果活細胞周圍八個位置的活細胞數少於兩個,則該位置活細胞死亡;
  2. 如果活細胞周圍八個位置有兩個或三個活細胞,則該位置活細胞仍然存活;
  3. 如果活細胞周圍八個位置有超過三個活細胞,則該位置活細胞死亡;
  4. 如果死細胞周圍正好有三個活細胞,則該位置死細胞復活;

根據當前狀態,寫一個函式來計算面板上細胞的下一個(一次更新後的)狀態。下一個狀態是通過將上述規則同時應用於當前狀態下的每個細胞所形成的,其中細胞的出生和死亡是同時發生的。

示例:

輸入: 
[
  [0,1,0],
  [0,0,1],
  [1,1,1],
  [0,0,0]
]
輸出: 
[
  [0,0,0],
  [1,0,1],
  [0,1,1],
  [0,1,0]
]

進階:

  • 你可以使用原地演算法解決本題嗎?請注意,面板上所有格子需要同時被更新:你不能先更新某些格子,然後使用它們的更新後的值再更新其他格子。
  • 本題中,我們使用二維陣列來表示面板。原則上,面板是無限的,但當活細胞侵佔了面板邊界時會造成問題。你將如何解決這些問題?

解題思路:

位運算,原地修改。原則上,資料是變化的,每一個點的下一個時刻需要上一個時刻的周圍資料才能計算出下一個資料,講道理需要兩個陣列。但是,資料只有0,1兩種情況,可以通過位的左移,二進位制的第二位上儲存上一個值,然後新的值儲存在第一位,或者相反。之後再更新資料即可。這是一道經典的原地修改演算法,通過值的巧妙變換達到原地修改的目的。

       

C++程式碼
class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        int sizex = board.size();
        if (sizex == 0) return;
        int sizey = board[0].size();
        if (sizey == 0) return;
        int i, j;
        for (i = 1; i <= sizex; i++) {//重新整理結點
            for (j = 1; j <= sizey; j++) {
                board[i - 1][j - 1] <<= 1;
            }
        }
        for (i = 1; i <= sizex; i++) {//地位計算新的值
            for (j = 1; j <= sizey; j++) {
                int U = 0, UL = 0, UR = 0, L = 0, R = 0, DL = 0, D = 0, DR = 0;
                if (i > 1) U = board[i - 2][j - 1]>>1;
                if (i > 1 && j > 1) UL = board[i - 2][j - 2]>>1;
                if (i > 1 && j < sizey)UR = board[i - 2][j]>>1;
                if (j > 1) L = board[i - 1][j - 2]>>1;
                if (j < sizey) R = board[i - 1][j]>>1;
                if (j > 1 && i < sizex) DL = board[i][j - 2]>>1;
                if (i < sizex) D = board[i][j - 1]>>1;
                if (i < sizex&&j < sizey) DR = board[i][j]>>1;
                int cout = U + UL + UR + L + R + DL + D + DR;
                if (cout < 2||cout>=4) board[i - 1][j - 1] |= 0;
                else if (board[i - 1][j - 1] == 2) {
                    board[i - 1][j - 1] |= 1;
                }
                else {
                    if(cout==3) board[i - 1][j - 1] |= 1;
                }
            }
        }
        for (i = 1; i <= sizex; i++) {//重新整理結點
            for (j = 1; j <= sizey; j++) {
                board[i - 1][j - 1] &= 1;
            }
        }
    }
};