• 关于sg函数打表的理解


    博弈的题大多数用sg函数打表找规律
    博弈的题大多数用sg函数打表找规律
    博弈的题大多数用sg函数打表找规律
    记忆话搜索可以更快
    记忆话搜索可以更快
    记忆话搜索可以更快

    理解

    定义sg值为0时表示后手必胜,sg为1时为先手必胜。
    那么对于每一个人,都会去查找使得当前状态变成sg值为0的情况
    那么就是说,对于多种情况,只会选择sg值最小的情况进行选择,遍历一下,看看以我为起点进行,最终到达的情况是如果是sg值为0,那么我就进行选择,否则只能任意选择

    第一种打表写法。
    从现状态开始进行查找。

    1. 对于必败方的情况都返回0,此时的必败状态就是一眼能看出来的情况,即不用博弈就知道先手必胜的情况。
    2. 然后令sg值为1,表示我必输状态,再取判断所有情况,得到所有情况的sg值,取最小值
    3. 转换状态:如果对于下一个人来说,当前sg为0,即后手必胜,即我胜利,返回值为1,否则sg值为1时,表示先手必胜,也就是我必输。
      然后打表找规律即可。

    第二种打表写法
    从先手必败的局势开始往后递推

    2020ccpc绵阳的G题
    有0123这4种数字a,b,c,d张,每次可以拿走两个数字,和≤3,然后用两个数字的和来代替,放回数字堆。两个人进行博弈
    sg函数打表

    int sg(int a, int b, int c, int d){
        if(a == 0 && b == 0) return 0;
        if(a == 1 && b == 0 && c == 0 && d == 0) return 0;
        if(a == 0 && b == 1 && c == 0) return 0;
        int f = 1;
        if(a > 0) f = min(f, sg(a - 1, b, c, d));
        if(b >= 2) f = min(f, sg(a, b - 2, c + 1, d));
        if(b > 0 && c > 0) f = min(f, sg(a, b - 1, c - 1, d + 1));
        return 1 - f;
    }
    

    然后直接打表得到规律。

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int sg(int a, int b, int c, int d){
        if(a == 0 && b == 0) return 0;
        if(a == 1 && b == 0 && c == 0 && d == 0) return 0;
        if(a == 0 && b == 1 && c == 0) return 0;
        int f = 1;
        if(a > 0) f = min(f, sg(a - 1, b, c, d));
        if(b >= 2) f = min(f, sg(a, b - 2, c + 1, d));
        if(b > 0 && c > 0) f = min(f, sg(a, b - 1, c - 1, d + 1));
        return 1 - f;
    }
    bool check(int a, int b, int c, int d){
        if(a == 0 && b == 0 && c == 0 && d == 0) return 0;
        if(b == 0 && c == 0 && d == 0) {
            return a % 2 == 0;
        }
        if(a & 1) {
            if(b % 3 == 0) return 1;
            if(b % 3 == 1) return c == 0;
            if(b % 3 == 2) return c >= 2;
            return 1;
        }else {
            if(b % 3 == 0) return 0;
            if(b % 3 == 1) return c != 0;
            if(b % 3 == 2) return 1;
            return 1;
        }
        return 1;
    }
    int main(){
        int t;
        scanf("%d", &t);
        for(int i = 1; i <= t; i++) {
            int a, b, c, d;
            scanf("%d%d%d%d", &a, &b, &c, &d);
            printf("Case #%d: %s
    ", i, check(a, b, c, d) ? "Rabbit" : "Horse");
        }
        return 0;
    }
    

    传送门
    直接sg打表就行了,必败情况是n = 1时
    因为考虑到答案是求≥n时结束,也相当于是除以某个数字向上取整的意思
    记忆话搜索一下防止超时

    #include <iostream>
    #include <cstdio>
    #include <map>
    #define ll long long
    using namespace std;
    const int N = 1e5 + 4;
    std::map<ll, bool> mp;
    bool SG(ll n){
        if(n == 1) return 0;
        if(n <= 9) return 1;
        if(n <= 18) return 0;
        if(mp.count(n)) return mp[n];
        bool f = 1;
        for(int i = 2; i <= 9; i++) {
            f = min(f, SG((n + i - 1) / i));
        }
        return mp[n] = 1 - f;
    }
    int main(){
        ll n;
        while(~scanf("%lld", &n)){
            printf("%s
    ", SG(n)? "Stan wins.":"Ollie wins.");
        }
        return 0;
    }
    

    传送门
    这个可以用第二种打表方法进行
    从先手必败的(0,0,0)进行递推

    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int N = 305;
    bool sg[N][N][N];
    void init(){
        int a = 300, b = 300, c = 300;
        for(int i = 0; i <= 300; i++){
            for(int j = 0; j <= 300; j++){
                for(int k = 0; k <= 300; k++){
                    if(!sg[i][j][k]){
                        for(int x = 1; x + i <= a; x++) sg[x + i][j][k] = 1;
                        for(int y = 1; y + j <= b; y++) sg[i][y + j][k] = 1;
                        for(int z = 1; z + k <= c; z++) sg[i][j][z + k] = 1;
                        for(int x = 1; x + i <= a && x + j <= b; x++) sg[i + x][j + x][k] = 1; 
                        for(int x = 1; x + i <= a && x + k <= c; x++) sg[i + x][j][k + x] = 1;
                        for(int x = 1; x + j <= b && x + k <= c; x++) sg[i][j + x][k + x] = 1; 
                    }
                }
            }
        }
    }
    int main(){
        init();
        int a, b, c;
        while(cin >> a >> b >> c){
            printf("%d
    ", sg[a][b][c]);
        }
        return 0;
    }
    
  • 相关阅读:
    lms框架服务注册中心
    lms框架应用服务接口和服务条目详解
    lms框架模块详解
    lms微服务框架主机介绍
    lms框架分布式事务使用简介
    MySQL锁总结
    VSCode 输出 java SSL 请求过程
    PowerShell自动化发布SpringBoot项目到Tomcat
    AWS EC2 使用---安装Docker和Nginx
    使用PowerShell连接到Linux
  • 原文地址:https://www.cnblogs.com/Emcikem/p/13916936.html
Copyright © 2020-2023  润新知