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;
}