「THUPC2018」生生不息 / Lives(状压 + 记忆化搜索 + 打表)

题目链接: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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值