蓝桥杯------2017 Java B组 国赛:第二题 生命游戏

题目描述:

康威生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。  
这个游戏在一个无限大的2D网格上进行。

初始时,每个小方格中居住着一个活着或死了的细胞。
下一时刻每个细胞的状态都由它周围八个格子的细胞状态决定。

具体来说:

1. 当前细胞为存活状态时,当周围低于2个(不包含2个)存活细胞时, 该细胞变成死亡状态。(模拟生命数量稀少)
2. 当前细胞为存活状态时,当周围有2个或3个存活细胞时, 该细胞保持原样。
3. 当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
4. 当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。 (模拟繁殖)

当前代所有细胞同时被以上规则处理后, 可以得到下一代细胞图。按规则继续处理这一代的细胞图,可以得到再下一代的细胞图,周而复始。

例如假设初始是:(X代表活细胞,.代表死细胞)
.....
.....
.XXX.
.....

下一代会变为:
.....
..X..
..X..
..X..
.....

康威生命游戏中会出现一些有趣的模式。例如稳定不变的模式:

....
.XX.
.XX.
....

还有会循环的模式:

......      ......       ......
.XX...      .XX...       .XX...
.XX...      .X....       .XX...
...XX.   -> ....X.  ->   ...XX.
...XX.      ...XX.       ...XX.
......      ......       ......


本题中我们要讨论的是一个非常特殊的模式,被称作"Gosper glider gun":

......................................
.........................X............
.......................X.X............
.............XX......XX............XX.
............X...X....XX............XX.
.XX........X.....X...XX...............
.XX........X...X.XX....X.X............
...........X.....X.......X............
............X...X.....................
.............XX.......................
......................................

假设以上初始状态是第0代,请问第1000000000(十亿)代一共有多少活着的细胞?

注意:我们假定细胞机在无限的2D网格上推演,并非只有题目中画出的那点空间。
当然,对于遥远的位置,其初始状态一概为死细胞。

注意:需要提交的是一个整数,不要填写多余内容。

     首先总结认真审题,总结题目的信息,知道细胞分死亡存活两种状态,对于死亡的细胞只要周围(八个方向)存活细胞数等于3,则该细胞能恢复活性;而对于存活的细胞,周围存活细胞数小于2或者大于3则会死亡,周围存活细胞数等于2或者3才能存活

思路:

    鉴于这只是一道填空题,所以就不去考虑什么骚操作了,首先把"Gosper glider gun"存入记事本中,再读取来处理,用一个二维整形数组存储这个表,存活细胞记为 1死亡细胞则记为 0

    然后再用一个LinkedList来存存活的细胞坐标位置,这样方便每一代更替时进行处理,毕竟只有当周围有存活细胞时,死亡细胞才有可能复活,然后就是不断循环重复对每个存活的细胞及其周边八个方向的死亡细胞进行判断,判断死亡细胞是否会复活,存活细胞是否会死亡。

    得到其每一代的存活细胞数量,观察其细胞增长是否有规律。

 

代码如下:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedList;	

public class Main {
	public static final int MAX_NUMBER = 1000;
	
	public static int[][] map = new int[MAX_NUMBER][MAX_NUMBER];
	public static int[][] tmpMap;
	public static int[][] beenCheck;
	
	public static LinkedList<coor> life = new LinkedList<coor>();
	
	public static final int[][] dir = new int[][] {{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
	
	//自定义的坐标类
	public static class coor {
		public int x;
		public int y;
		coor(int x, int y) {
			this.x = x;
			this.y = y;
		}
	}
	
	//判断当前存活细胞的去留
	public static void Judge(coor c) {
		
		int countLife = 0;
		
		for(int i = 0; i < dir.length; i++) {
			//周围的存活细胞
			if(map[c.x + dir[i][0]][c.y + dir[i][1]] == 1) {
				countLife++;
			}
			//周围的死亡细胞
			else {
				//尚未被检查过
				if(beenCheck[c.x + dir[i][0]][c.y + dir[i][1]] == 0) {
					int countDead = 0;
					int tmpR = c.x + dir[i][0];
					int tmpC = c.y + dir[i][1];
					//遍历该死亡细胞的八个方向
					for(int j = 0; j < dir.length; j++) {
						if(map[tmpR + dir[j][0]][tmpC + dir[j][1]] == 1) {
							countDead++;
						}
					}
					
					//周围细胞数为3,死亡细胞翻身
					if(countDead == 3) {
						tmpMap[tmpR][tmpC] = 1;
						life.addLast(new coor(tmpR, tmpC));
					}
					//该点被检查标志
					beenCheck[tmpR][tmpC] = 1;
				}
			}
		}
		
		//对当前细胞的生死去留判断
		if(countLife == 3 || countLife == 2) {
			life.addLast(c);
			tmpMap[c.x][c.y] = 1;
		}
	}
	
	
	public static void main(String[] args) throws IOException {
		File file = new File("C:\\Users\\vMars\\Desktop\\input\\in.txt");
		BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
		String str = null;
		int row = 0;
		while((str = br.readLine()) != null) {
			System.out.println(str);
			//不要离边边太近,防止溢出
			int step = 10;
			for(int column = 0; column < str.length(); column++) {
				if(str.charAt(column) == 'X') {
					map[step + row][step + column] = 1;
					life.addLast(new coor(step+row, step+column));
				}
				else {
					map[step + row][step + column] = 0;
				}
			}
			row++;
		}
		
		//演变50代
		for(int i = 0; i <= 50; i++) {
			System.out.println(life.size());
			beenCheck= new int[MAX_NUMBER][MAX_NUMBER];
			tmpMap = new int[MAX_NUMBER][MAX_NUMBER];
			//记录当前的存活细胞个数
			int lifeSize = life.size();
			for(int j = 0; j < lifeSize; j++) {
				//这样就可以只判断当前存活的,而不影响后面经过判断得到新的存活细胞坐标(因为是尾插)
				Judge(life.getFirst());
				life.removeFirst();
			}
			map = tmpMap;
		}
				
		
		br.close();
	}
}

    从上面代码可以得到该图形50代的繁殖数量变化情况,将其存到Excel表中观察分析。

蓝桥杯------2017 Java B组 国赛:第二题 生命游戏

    可以发现该图的细胞繁殖数量每30代一次循环,且每30代净增长为5个存活细胞,所以可以通过计算器算:

1000000000  / 30 = 33333333.33333333

1000000000  % 30 = 10 ,通过表格计算可得,经过10代净增长为 12

所以结果等于:33333333*5 + 36(第0代)+12 = 166666713