最近也不知道在忙啥,反正好久沒玩 codewars 了,上去看看的時候偶爾看到了一個很有意思的題目,稍微看了一下說明,感覺可以做出來,于是就開始了長達 2+2 個小時的解題過程。
下面是簡單的題目說明:簡單的來說,就是有一個二維數組,數組的每一個元素就是一個細胞,細胞有存活和死亡兩種狀態,每經過一代,細胞的狀態都可以發生改變,有如下四個具體規則:
1、任何具有少于兩個活鄰居的活細胞都會死亡。
2、任何具有三個以上活鄰居的活細胞都會死亡。
3、任何有兩個或三個活鄰居的活細胞都可以存活到下一代。
4、任何具有恰好三個活鄰居的死細胞都將變為活細胞。
什么是鄰居呢?就是圍繞著該細胞的周圍一圈,即八個細胞。
好了,在看完這些的時候,我的思路差不多就成型了。首先,我得獲取到這個細胞周圍細胞的狀態,并且判斷有幾個活細胞,這樣就能夠根據規則判斷各個細胞下一代的存活情況了。當我刷刷刷敲下代碼,順利地通過第一個測試的時候,我得意地提交了我的代碼,結果出乎我的意料,在第三個測試之后陸續出現了問題。從此,我開始了長達半個小時的debug,但是仍然一無所獲,只好絕望地看看討論區的各位,結果,我發現了一點玄機!
首先,仔細點的朋友(不像我)就會發現,這道題的題目叫做 Conway's Game of Life - Unlimited Edition,而我目前的思路,只是前半部分,并沒有體現出Unlimited Edition(無限版)。發現了這個之后,我仔細地第無數次讀題目,終于找到了一個關鍵信息,即這個區域是無限的,出了題目給的細胞之外,還有無數的死細胞,再通過第四條規則,死細胞是有機會變成活細胞的,即會對結果產生巨大的影響!
發現了這點之后,我開始意識到,這道題不像我原先現象的那么簡單,以下這個動畫圖能夠生動地說明這個問題。我們要關心的不只是這個小小的細胞區域,而是整個可能無限的區域。
接下來,正片開始!我的思路很簡單,首先著眼于死細胞唯一的再生途徑,即規則四,附近有恰好三個活細胞。把關注點放在這里之后,不難想到,這個區域不是無限大的了,而頂多只是比原先的區域大一圈而已,然后再根據每一代的存活情況,慢慢變大或者不變。思路理清楚了之后,實現起來就不難了,我把優化過的代碼放在這里。雖然能夠解決問題,但是感覺還是不夠完美,看了好久別人的代碼,感覺大體思想都差不多,只是把函數封裝了一下,看起來舒服一點。如果大家有更好的想法,歡迎找我談論!
function get_generation(array $cells, int $generations): array {
? $x = count($cells);
? $y = count($cells[0]);
? while($generations--) {
? ? //零數組,長度為
? ? $emptyX = array_fill(0,count($cells[0]),0);
? ? //如果第一行中活細胞數大于等于3,則有可能周圍死細胞會再生,需要在其前方插入一行零數組
? ? if(array_sum($cells[0]) >= 3) array_unshift($cells,$emptyX);
? ? //如果最后一行中活細胞數大于等于3,則有可能周圍死細胞會再生,需要在其后方插入一行零數組
? ? if(array_sum($cells[count($cells)-1]) >= 3) array_push($cells,$emptyX);
? ? //如果第一列中活細胞數大于等于3,則有可能周圍死細胞會再生,需要在其前方插入一列零數組
? ? while($cells != [] && array_sum(array_column($cells,0)) >= 3) {
? ? ? foreach($cells as $key=>$item) {
? ? ? ? array_unshift($cells[$key],0);
? ? ? }
? ? }
? ? //如果最后一列中活細胞數大于等于3,則有可能周圍死細胞會再生,需要在其后方插入一列零數組
? ? $i = count($cells[0])-1;
? ? while($cells != [] && array_sum(array_column($cells,$i)) >=3 ) {
? ? ? foreach($cells as $key=>$item) {
? ? ? ? array_push($cells[$key],0);
? ? ? }
? ? ? $i = count($cells[0])-1;
? ? }
? ? $x = count($cells);
? ? $y = count($cells[0]);
? ? $n = 0;
? ? $array = [];
? ? for($i=0;$i<$x;$i++) {
? ? ? for($j=0;$j<$y;$j++) {
? ? ? ? //每個細胞目前的存活情況
? ? ? ? $array['self'][$n] = $cells[$i][$j];
? ? ? ? for($k=($i-1>=0?$i-1:0);$k<=($i+1<$x-1?$i+1:$x-1);$k++) {
? ? ? ? ? for($l=($j-1>=0?$j-1:0);$l<=($j+1<$y-1?$j+1:$y-1);$l++) {
? ? ? ? ? ? //統計每個細胞周圍的存活情況
? ? ? ? ? ? $array['around'][$n] += $cells[$k][$l];
? ? ? ? ? }
? ? ? ? }
? ? ? ? $n++;
? ? ? }
? ? }
? ? //通過規則,判斷每個細胞下一代的存活情況
? ? for($i=0;$i<$x*$y;$i++) {
? ? ? $array['around'][$i] -= $array['self'][$i];
? ? ? if($array['around'][$i] < 2) {
? ? ? ? $array['self'][$i] = 0;
? ? ? } elseif ($array['around'][$i] > 3) {
? ? ? ? $array['self'][$i] = 0;
? ? ? } elseif ($array['around'][$i] == 3) {
? ? ? ? $array['self'][$i] = 1;
? ? ? }
? ? }
? ? $n = 0;
? ? //新的一代
? ? for($i=0;$i<$x;$i++) {
? ? ? for($j=0;$j<$y;$j++) {
? ? ? ? $cells[$i][$j] = $array['self'][$n];
? ? ? ? $n++;
? ? ? }
? ? }
? }
? //如果第一行為空行,則可以刪除
? while($cells != [] && empty(array_sum($cells[0]))) {
? ? array_splice($cells, 0, 1);
? }
? //如果最后一行為空行,則可以刪除
? while($cells != [] && empty(array_sum($cells[count($cells)-1]))) {
? ? array_splice($cells, count($cells)-1, 1);
? }
? //如果第一列為空列,則可以刪除
? while($cells != [] && empty(array_sum(array_column($cells,0)))) {
? ? for($j=0;$j<count($cells);$j++) {
? ? ? array_splice($cells[$j], 0, 1);
? ? }
? }
? //如果最后一列為空列,則可以刪除
? $i = count($cells[0])-1;
? while($cells != [] && empty(array_sum(array_column($cells,$i)))) {
? ? for($j=0;$j<count($cells);$j++) {
? ? ? array_splice($cells[$j], $i, 1);
? ? }
? ? $i = count($cells[0])-1;
? }
? //如果沒有細胞存活,則返回二維的空數組
? $sum = 0;
? for($i=0;$i<$x;$i++) {
? ? for($j=0;$j<$y;$j++) {
? ? ? $sum += $cells[$i][$j];
? ? }
? }
? return $sum == 0 ? [[]] : $cells;
}
另外,如果你有興趣,或者是有問題想要與我探討,歡迎來訪問我的博客:https:mu-mu.cn/blog