2017年第八届蓝桥杯Java程序设计本科B组决赛个人题解汇总:
https://blog.csdn.net/daixinliangwyx/article/details/90184941
第二题
标题:生命游戏
康威生命游戏是英国数学家约翰·何顿·康威在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网格上推演,并非只有题目中画出的那点空间。
当然,对于遥远的位置,其初始状态一概为死细胞。
注意:需要提交的是一个整数,不要填写多余内容。
解法:题目要求十亿代,所以肯定是不能直接模拟每一代的,毕竟图也开不了这么大,因此考虑繁殖是否存在某种规律。先将初始状态的图扩大一点使初始状态在整个图的中间,方便后续繁殖扩散,先模拟一些代数的繁殖,观察每一代存活细胞的个数与上一代的差别,将结果输出到txt文件中,可以发现,前几代(从第一代开始)的差别是3、4、5、3、-7、7...,而从31代开始的差别是3、4、5、3、-7、7...,从61代开始的差别又是3、4、5、3、-7、7...,因此可以知道,细胞繁殖的差别每过30代又重复一次,而每经过30代的繁殖后存活细胞的数量就增加5,因此就可以来直接计算十亿代后的存活细胞数量了。
模拟100代繁殖的代码:
#include<bits/stdc++.h>
using namespace std;
char Map[50][100], next[50][100];
int len, n = 50, m = 100, sum, presum;
string s;
int dir[8][2] = {-1, -1, -1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 0, 1, 1};
void dfs(int x, int y) {
sum = 0;
if(x == n && y == 0) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// cout << next[i][j];
if (next[i][j] == 'X') sum++;
}
// cout << endl;
}
cout << "存活细胞数量: " << sum << " 比上一代繁殖了 " << sum-presum << endl;
presum = sum;
return;
}
int num = 0;
for(int i = 0; i < 8; i++) {
int nextx = x + dir[i][0];
int nexty = y + dir[i][1];
if (nextx >= 0 && nextx < n && nexty >= 0 && nexty < m && Map[nextx][nexty] == 'X') num++;
}
if (Map[x][y] == '.' && num == 3) next[x][y] = 'X';
else if (Map[x][y] == 'X' && (num < 2 || num > 3)) next[x][y] = '.';
else
next[x][y] = Map[x][y];
if (y+1 >= m) dfs(x+1, 0);
else
dfs(x, y+1);
}
int main() {
freopen("F:\\out.txt", "w", stdout); //输出重定向,输出数据将保存在F盘中的out.txt文件中
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
Map[i][j] = '.';
sum = 0;
for (int i = 11; i < 22; i++) {
getline(cin, s);
len = s.size();
for (int j = len; j < len*2; j++) {
Map[i][j] = s[j-len];
if (Map[i][j] == 'X') sum++;
}
}
cout << "第0代:";
// for (int i = 0; i < n; i++) {
// for (int j = 0; j < m; j++)
// cout << Map[i][j];
// cout << endl;
// }
cout << "存活细胞数量: " << sum << " 开始繁殖 " << endl;
presum = sum;
for (int i = 1; i < 100; i++) {
cout << "第" << i << "代: ";
dfs(0, 0);
for (int ii = 0; ii < n; ii++)
for (int jj = 0; jj < m; jj++)
Map[ii][jj] = next[ii][jj];
}
fclose(stdout);//关闭输出重定向
return 0;
}
计算答案:
#include<bits/stdc++.h>
using namespace std;
long long add[31] = {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};
int main() {
long long ans = 36;
ans += 1000000000 / 30 * 5;//先算有多少个30代,每代增加5个也就是再*5,千万不能先乘再除因为这样会算错代数
for (long long i = 1; i <= 1000000000%30; i++)
ans += add[i];
cout << ans << endl;
return 0;
}