zoukankan      html  css  js  c++  java
  • 求SG函数(两种方法)

    SG函数

    定义

    首先我们定义 (mex{}) 运算,计算结果为除当前集合外的最小的非负整数(即包括0)。

    例如 (mex){1, 2, 4} = 0,(mex){0, 1, 2} = 3。

    SG函数就是这个运算的值。

    假设在一个 (DAG) 上,(SG[x] = mex{SG[y] | y 是 x 的后继})。若 (SG[x] = 0) 那么当前回合的人必败,反之必胜。

    另一个例子,在取石子游戏中,能取的个数就是我们 (mex) 中的值。

    具体详见这位大佬的博客,个人认为讲的非常清楚 博弈论(SG)

    求解

    打表法

    例题:HDU 1847
    Description

    vjudge(传送门)

    Solution

    这道题目中最多有1000张牌,每次只能拿 2 的倍数张,我们预处理能摸的牌数 (2^0) ~ (2^9) 即可。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int N = 1010;
    int n;
    int s[10], sg[N * 10], vis[N * 10];        //vis就是mex中的值
    
    void getSG(int n){
        for(int i = 1; i <= n; i++){
            memset(vis, 0, sizeof(vis));
            for(int j = 0; j <= 9 && s[j] <= i; j++)
                vis[sg[i - s[j]]] = 1;        //i-s[j]是i的后继,即上文中提到的y
            for(int j = 0; j <= 9; j++)
                if(!vis[j]){                //第一个为0的非负整数就是SG值
                    sg[i] = j;
                    break;
                }
        }
    }
    
    int main(){
        for(int i = 0; i <= 9; i++)        //预处理
            s[i] = (1 << i);
        getSG(1000);                    //求SG
        while(scanf("%d", &n) != EOF)
            puts(sg[n] ? "Kiki" : "Cici");
        return 0;
    }
    

    dfs法

    例题:HDU 1536

    vjudge(传送门)

    Description

    首先输入 (K) 表示一个集合的大小 之后输入集合 (s) 表示对于这对石子只能取这个集合中的元素的个数。

    之后输入一个 (m) 表示接下来对于这个集合要进行 (m) 次询问。

    之后 (m) 行 每行输入一个 (n) 表示有 (n) 个堆 每堆有 (x) 个石子 问这一行所表示的状态是赢还是输 如果赢输入 (W) 否则 (L)

    多组输入,直到 (k=0) 时结束

    Solution

    对于 (n) 堆石子,分别处理,最后让每一堆异或起来即可,如果不为0那么必胜状态,反之为必败状态。

    (dfs)(sg) 初值为 -1,集合 (s) 要从小到大排序。

    #include<iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 110;
    const int M = 1e4 + 10;
    int n, m, k;
    int sg[M], s[N];
    
    int getSG(int x){
        if(sg[x] != -1) return sg[x];
        bool vis[N] = {0};
        for(int i = 1; i <= k; i++)
            if(x >= s[i])
                vis[getSG(x - s[i])] = 1;        //x的后继向x转移
        for(int i = 0; i < N; i++)
            if(!vis[i]){
                sg[x] = i;
                break;
            }
        return sg[x];
    }
    
    int main(){
        while(scanf("%d", &k) && k){
            for(int i = 1; i <= k; i++)
                scanf("%d", &s[i]);
            sort(s + 1, s + 1 + k);
            scanf("%d", &m);
            memset(sg, -1, sizeof(sg));
            while(m--){
                scanf("%d", &n);
                int ans = 0, x;
                for(int i = 1; i <= n; i++){
                    scanf("%d", &x);
                    ans ^= getSG(x);
                }
                if(ans) printf("W");
                else  printf("L");
            }
            printf("
    ");
        }
        return 0;
    }
    

    [\_EOF\_ ]

    本文来自博客园,作者:xixike,转载请注明原文链接:https://www.cnblogs.com/xixike/p/15122060.html

  • 相关阅读:
    滑动切换界面---多个Activity
    172. Factorial Trailing Zeroes
    152. Maximum Product Subarray
    149. Max Points on a Line
    [转载][c++]用宏(Macro)定义一个函数
    [转载][C++]C++11 左值、右值、右值引用详解
    [转载][c++]C++中指针常量和常量指针的区别
    [转载][C++]类构造函数初始化列表
    22. Generate Parentheses
    328. Odd Even Linked List
  • 原文地址:https://www.cnblogs.com/xixike/p/15122060.html
Copyright © 2011-2022 走看看