zoukankan      html  css  js  c++  java
  • 关于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;
    }
    
  • 相关阅读:
    HDU1720 A+B Coming
    HDU1390 ZOJ1383 Binary Numbers
    HDU1390 ZOJ1383 Binary Numbers
    HDU2504 又见GCD
    HDU2504 又见GCD
    HDU1335 POJ1546 UVA389 UVALive5306 ZOJ1334 Basically Speaking
    HDU1335 POJ1546 UVA389 UVALive5306 ZOJ1334 Basically Speaking
    HDU1020 ZOJ2478 Encoding
    HDU1020 ZOJ2478 Encoding
    HDU2097 Sky数
  • 原文地址:https://www.cnblogs.com/Emcikem/p/13916936.html
Copyright © 2011-2022 走看看