• SG函数学习笔记


    概念

    一.必胜态与必败态

    1. 必胜态(N),最优策略下谁面临此状态谁必赢。
    2. 必败态(P),最优策略下谁面临此状态谁必输。

    二.SG函数

    1. mex运算:定义为求最小的不属于此集合的非负整数。
    2. SG(x)中x表示状态。x是必败态当且仅当SG(x) = 0;
    3. 令 S 为所有x走一步可以到达的状态(必须合法)。那么有$SG(x) = mex(SG(y))(y ∈ S) $
    4. 可以暴力求骗分,主要是打表找规律

    三.SG定理

    1. 游戏和的 SG 函数就是所有子游戏的 SG 函数的异或和,其中所有子游戏互相独立。
    2. 所以先手必胜当且仅当每堆石子的 SG 函数的异或和不为 0。

    例题

    取火柴游戏

    分析

    1. 由SG定理判断初始状态,设SG = a1a2a3...an.如果SG == 0就lose
    2. 如果必胜,假设是a1中某些被取走,就令a1变成a'。有a'a2a3...an = 0 = SG ^ SG = SGa1a2a3...^an;
    3. 所以SG^a1 = a'.且必有a1 > a';
    4. 所以一旦有SG^ai < ai,就是在第i堆取走ai - SG^ai个。
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define lson x<<1
    #define rson x<<1|1
    #define ll long long
    #define rint register int
    #define mid  ((L + R) >> 1)
    using namespace std;
    template <typename xxx> inline void read(xxx &x) {
    	char c = getchar(),f = 1;x = 0;
    	for(;c ^ '-' && !isdigit(c);c = getchar());
    	if(c == '-') c = getchar(),f = -1;
    	for(;isdigit(c);c = getchar()) x = (x<<1) + (x<<3) + (c ^ '0');
    	x *= f;
    }
    template<typename xxx>void print(xxx x)
    {
        if(x<0){putchar('-');x=-x;}
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    const int maxn = 500010;
    const int inf = 0x7fffffff;
    const int mod = 1e9 + 7;
    int SG;
    int n,a[maxn];
    int main()
    {
    	read(n);
    	for(rint i = 1;i <= n; ++i) read(a[i]),SG ^= a[i];
    	if(!SG) {//当前即是必败态
    		printf("lose
    ");
    		return 0; 
    	} 
    	for(rint i = 1;i <= n; ++i) {
    		if((SG ^ a[i]) < a[i]) {
    			printf("%d %d
    ",a[i] - (SG ^ a[i]),i);
    			for(rint j = 1;j <= n; ++j) {
    				if(i ^ j) print(a[j]);
    				else print(a[i] ^ SG);
    				putchar(' ');
    			}
    			return 0;
    		}	
    	}
    	return 0;
    }
    

    G - Game of Cards

    分析

    暴力求解SG函数

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<bitset>
    #include<cstring>
    #include<algorithm>
    #define lson x<<1
    #define rson x<<1|1
    #define ll long long
    #define rint register int
    #define mid  ((L + R) >> 1)
    using namespace std;
    template <typename xxx> inline void read(xxx &x) {
    	char c = getchar(),f = 1;x = 0;
    	for(;c ^ '-' && !isdigit(c);c = getchar());
    	if(c == '-') c = getchar(),f = -1;
    	for(;isdigit(c);c = getchar()) x = (x<<1) + (x<<3) + (c ^ '0');
    	x *= f;
    }
    template<typename xxx>void print(xxx x)
    {
        if(x<0){putchar('-');x=-x;}
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    const int maxn = 1010;
    const int inf = 0x7fffffff;
    const int mod = 1e9 + 7;
    int n,m,k;
    int a[maxn];
    int SG[maxn];
    bitset<maxn>tem;
    inline int solve() {
    	for(rint i = 1;i <= n; ++i) {//枚举SG[x]的x
    		SG[i] = 0;//初始化
    		tem.reset();
    		for(rint j = 0;j <= k && j < i; ++j) {//枚举可以转移的先前状态
    			if(i - j - a[i - j] >= 0) {
    				tem[SG[i - j - a[i - j]]] = 1;
    			}
    		}
    		for(rint j = 0;; ++j) {
    			if(!tem[j]) {//找最小非负整数
    				SG[i] = j;
    				break; 
    			}
    		} 
    	}
    	return SG[n];
    }
    int main()
    {
    	int t;
    	read(t);read(k);
    	int ans = 0;
    	while(t--) {
    		read(n);
    		for(rint i = 1;i <= n; ++i) read(a[i]);
    		ans ^= solve();
    	}
    	if(ans) printf("Alice can win.");
    	else printf("Bob will win.");
    	return 0;
    }
    

    HDU-1730

    分析

    观察发现距离为0时SG[0] = 0,可以考虑以距离做nim游戏;

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define lson x<<1
    #define rson x<<1|1
    #define ll long long
    #define rint register int
    #define mid  ((L + R) >> 1)
    using namespace std;
    template <typename xxx> inline void read(xxx &x) {
    	char c = getchar(),f = 1;x = 0;
    	for(;c ^ '-' && !isdigit(c);c = getchar());
    	if(c == '-') c = getchar(),f = -1;
    	for(;isdigit(c);c = getchar()) x = (x<<1) + (x<<3) + (c ^ '0');
    	x *= f;
    }
    template<typename xxx>void print(xxx x)
    {
        if(x<0){putchar('-');x=-x;}
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    const int maxn = 100010;
    const int inf = 0x7fffffff;
    const int mod = 1e9 + 7;
    int n,m;
    int ti,ji;
    int main()
    {
    	while(~scanf("%d%d",&n,&m)) {
    		int ans = 0;
    		for(rint i = 1;i <= n; ++i) {
    			read(ti);
    			read(ji);
    			ans ^=(int)(abs(ti - ji) - 1);
    		}
    		if(ans) printf("I WIN!
    ");
    		else printf("BAD LUCK!
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    Pass360[最新].cpp
    在任意的远程桌面的session中运行指定的程序
    C++内存泄露的检测(三)
    在release模式下debug
    [转]VC Studio 使用技巧大全
    C++内存泄露的检测(一)
    获取系统用户所对应的配置路径
    关于Debug和Release之本质区别的讨论
    GDB 的使用
    struct termios
  • 原文地址:https://www.cnblogs.com/Thomastine/p/11798499.html
Copyright © 2020-2023  润新知