康威生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。
这个游戏在一个无限大的2D网格上进行。
初始时,每个小方格中居住着一个活着或死了的细胞。
下一时刻每个细胞的状态都由它周围八个格子的细胞状态决定。
具体来说:
- 当前细胞为存活状态时,当周围低于2个(不包含2个)存活细胞时, 该细胞变成死亡状态。(模拟生命数量稀少)
- 当前细胞为存活状态时,当周围有2个或3个存活细胞时, 该细胞保持原样。
- 当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
- 当前细胞为死亡状态时,当周围有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网格上推演,并非只有题目中画出的那点空间。
当然,对于遥远的位置,其初始状态一概为死细胞。
注意:需要提交的是一个整数,不要填写多余内容。
首先10亿次,暴力了一波,发现几分钟没出来结果,然后打表看了下结果(每次记录下一次状态比上一次多的个数,也就是记录的值是此时的状态的总数减去上一个状态的总数,)
//生命游戏
import java.math.BigInteger;
import java.util.*;
public class Main3 {
static char[][] mp = new char[300][300];
static int[] dx = {1, 1, 1, -1, -1, -1, 0, 0};
static int[] dy = {1, -1, 0, 1, -1, 0, 1, -1};
static ArrayList<Integer> result = new ArrayList<Integer>();
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
ArrayList<point> list = new ArrayList<point>();//当前的所有的点
ArrayList<point> tmp = new ArrayList<point>();//过渡用
int n = 11, m = 0;
for (int i = 0; i < 11; i++) {
String s = in.next();
m = s.length();
for (int j = 0; j < s.length(); j++) {
mp[i][j] = s.charAt(j);
if (mp[i][j] == 'X') {
list.add(new point(i, j));
}
}
}
for (int p = 0; p < 200; p++) {//循环次数
result.add(list.size());
tmp = new ArrayList<point>();
for (int i = 0; i < list.size(); i++) {//遍历当前所有存活的点
point cas = list.get(i);//遍历当前这个存活的点的周围的8个点
int ans = 0;
for (int j = 0; j < 8; j++) {
int xx = cas.x + dx[j];
int yy = cas.y + dy[j];
if (list.contains(new point(xx, yy))) ans++;
else if (round(new point(xx, yy), list)) {
if (!tmp.contains(new point(xx, yy))) tmp.add(new point(xx, yy));
}
}
if (ans < 2 || ans > 3) continue;
if (ans == 2 || ans == 3 && !tmp.contains(cas)) tmp.add(cas);
}
list = tmp;
}
//check
for (int i = 1; i < result.size(); i++) {
System.out.print(result.get(i) - result.get(i - 1) + " ");
}
int ans = 0;
//最后输出所有的点
// System.out.println(list.size());
}
private static boolean round(point point, ArrayList<point> list) {
int ans = 0;
for (int i = 0; i < 8; i++) {
int xx = point.x + dx[i];
int yy = point.y + dy[i];
if (list.contains(new point(xx, yy))) ans++;
}
if (ans == 3) return true;
return false;
}
static class point {
int x;
int y;
point() {}
point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if ((point)obj instanceof point) {
if (((point)obj).x == this.x && ((point)obj).y == this.y) {
return true;
}
}
return false;
}
}
}
得到以下结果:
3 4 5 3 -7 7 -3 13 -19 6 2 4 1 1 -14 2 3 6 1 0 0 -5 11 -17 7 -3 0 3 -2 -7 3 4 5 3 -7 7 -3 13 -19 6 2 4 1 1 -14 2 3 6 1 0 0 -5 11 -17 7 -3 0 3 -2 -7 3 4 5 3 -7 7 -3 13 -19 6 2 4 1 1 -14 2 3 6 1 0 0 -5 11 -17 7 -3 0 3 -2 -7 3 4 5 3 -7 7 -3 13 -19 6 2 4 1 1 -14 2 3 6 1 0 0 -5 11 -17 7 -3 0 3 -2 -7 3 4 5 3 -7 7 -3 13 -19 6 2 4 1 1 -14 2 3 6 1 0 0 -5 11 -17 7 -3 0 3 -2 -7 3 4 5 3 -7 7 -3 13 -19 6 2 4 1 1 -14 2 3 6 1 0 0 -5 11 -17 7 -3 0 3 -2 -7 3 4 5 3 -7 7 -3 13 -19 6 2 4 1 1 -14 2 3 6 1
仔细一看我们发现,这个是有重复的那么就不需要进行10亿次了;
进行转换和打表如下:166666713
public class Main {
//变化的序列
static int[] change = {0, 3, 4, 5, 3, -7, 7, -3, 13, -19, 6, 2, 4, 1, 1, -14, 2, 3, 6, 1, 0, 0, -5, 11, -17, 7, -3, 0, 3, -2, -7};
public static void main(String[] args) {
int origin = 36;
int re_all = 0;
for (int i = 0; i <= 30; i++) {
re_all += change[i];
}
//每经过30次变化,存活的数量的变化是origin + re_all;
int k = 1000000000/30;//经过30次变化的次数;
int yu = 1000000000%30;
int ans = 0;//最后的存活总数
ans = (origin + re_all * k);
for (int i = 1; i <= yu; i++) {
ans = ans + change[i];
}
System.out.println(ans);
}
}