题目链接:https://loj.ac/problem/6398
题目大意:
生命游戏是一个经典的零玩家游戏。
游戏在一块 n \times mn×m 的方格棋盘上进行,初始时,棋盘上的一些格子中有生命,另一些格子中没有生命。
在新的一天开始时,如果一个格子周围的 88 个(边界上的格子也许不到 88 个)格子中,在前一天有恰好22个或33个格子中有生命,则这个格子上的生命可以继续生存,如果周围的格子中有33个格子在前一天有生命且这个格子中前一天没有生命,则会在这个格子中诞生新的生命。在其他情况下,该格子中原有的生命则会死去且不会产生新的生命。
如果在某一天,棋盘上所有的格子里都没有生命,显然,在接下来的时间里,所有的格子中再也不会有生命了,我们就说这些生命灭绝了。
现在,牛牛希望让菲菲来决定游戏开始时棋盘上的每个格子中是否有生命。
而他想知道,在游戏开始时,菲菲有多少种不同的方法使得棋盘上的生命永远也不会灭绝。
解题思路:
看到n和m都比较小, 最多有2^25中情况,但是还要判断是否存活,如此一来时间复杂度会超过1e8,考虑记忆化搜索加状压可以大大减少时间,但是1s内也是跑不完的,打表得答案即可。记忆化搜索大概要跑1分钟。注意记忆化数组vis有三种情况:此情况没有检察、检查了而且可以肯定会死绝、检查了不会死绝。检查过直接return就好。
代码如下:
# include <bits/stdc++.h>
using namespace std;
const int maxn = 1 << 26;
int vis[maxn];
int n, m, t;
int dir[8][2] = {-1, -1, -1, 1, -1, 0, 1, -1, 1, 0, 1, 1, 0, 1, 0, -1};
/*
bool check(int x, int y){
if(x >= 0 && y >= 0 && x < n && y < m) return true;
return false;
}
int dfs(int cur){
if(vis[cur] != -1) return vis[cur]; //这就是为什么要初始化为-1的缘故
vis[cur] = 1;
int tmp = 0;
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j){
if(cur & (1 << (i*m+j)) ){
int cnt = 0;
for(int k = 0; k < 8; ++k){
int x = i + dir[k][0];
int y = j + dir[k][1];
if(check(x, y))
cnt += (cur & (1 << (x*m+y)) ) ? 1 : 0;
}
if(cnt == 2 || cnt == 3) tmp |= (1 << (i*m+j));
} else {
int cnt = 0;
for(int k = 0; k < 8; ++k){
int x = i + dir[k][0];
int y = j + dir[k][1];
if(check(x, y))
cnt += (cur & (1 << (x*m+y)) ) ? 1 : 0;
}
if(cnt == 3) tmp |= (1 << (i*m+j));
}
}
if(tmp == 0) return vis[cur] = 0;
else return vis[cur] = dfs(tmp); //这里两句必须return,否则绝对出错
}*/
int main(){
std::ios::sync_with_stdio(false);
int res[5][5] = {
0,0,0,0,0,
0,5,18,73,267,
0,18,150,1533,11398,
0,73,1533,31828,469972,
0,267,11398,469972,12785753
};
cin >> t;
while(t--){
cin >> n >> m;
cout << res[n-1][m-1] << endl;
}
/*
for(int i = 1; i <= 5; ++i){
for(int j = 1; j <= 5; ++j){
n = i, m = j;
int ans = 0;
memset(vis, -1, sizeof(vis));
for(int k = 0; k < (1 <<(i*j)); ++k){
if(vis[k] == -1) dfs(k);
ans += vis[k];
}
cout << ans << endl;
}
}*/
return 0;
}