[SG函数]Split Game(2020江西省大学生程序设计竞赛)

一开始有一个 n × m n \times m n×m的纸,之后随着游戏的进行,会得到多张不同大小的网格纸,这样就可以记一张纸为一个游戏,各个游戏构成一个游戏的和。其中每一个游戏都是一个有向图游戏,不过我们要自己找到不能行动的局面。显然 1 ∗ 2 1*2 12 1 ∗ 3 1*3 13 2 ∗ 1 2*1 21 3 ∗ 1 3*1 31同理)无法再次分割,且其他矩形均可以分割成这两者之一,因此选取这两个局面作为必败局面。

对于一张 N × M N \times M N×M的矩形网格纸,我们可以枚举如何行动,然后得到两个子游戏,对两子游戏 S G SG SG值进行异或运算,就得到了裁剪后局面对应的SG值。对所有裁剪后局面的 S G SG SG值进行 m e x mex mex运算(将所有值记作一个集合,返回集合内不存在的最小的数)即可得到当前这张纸的SG值。

注意要避开 1 × 1 1 \times 1 1×1的局面

#include 
#define int long long
using namespace std;

#define win cout << "Alice" << endl
#define lose cout << "Bob" << endl

const int N = 200;
int sg[N][N];

void getsg(){
    memset(sg, -1, sizeof(sg));
    sg[1][1] = sg[1][2] = sg[2][1] = sg[1][3] = sg[3][1] = 0;
    for(int i = 1; i <= N; i++){
        for(int j = 1; j <= N; j++){
            set<int> st;
            for(int k = 1; k < i; k++){
                int lx = k, ly = j;
                int rx = i - k, ry = j;
                if((lx == 1 && ly == 1) || (rx == 1 && ry == 1)) continue;
                st.insert(sg[lx][ly] ^ sg[rx][ry]);
            }
            for(int k = 1; k < j; k++){
                int lx = i, ly = k;
                int rx = i, ry = j - k;
                if((lx == 1 && ly == 1) || (rx == 1 && ry == 1)) continue;
                st.insert(sg[lx][ly] ^ sg[rx][ry]);
            }
            int mex = 0;
            while(st.count(mex)) mex++;
            sg[i][j] = mex;
        }
    }
}


inline void solve(){
    getsg(); 
    int n, m;
    while(cin >> n >> m){
        if(sg[n][m]) win;
        else lose;
    }

}

signed main(){
    solve();
    return 0;
}

你可能感兴趣的:(博弈,AC,Road)