zoukankan      html  css  js  c++  java
  • 模板

    1.要从必胜或必败的局面反推

    2.SG函数

    只要当前状态可以转移到的状态中有一个是败态,那么当前状态就是胜态。胜态为N。

    如果当前状态可以转移到的所有状态都是胜态,那么当前状态就是败态。败态为P。

    sg函数为每个状态赋一个自然数的值,这个值为除这个状态的后继外最小自然数。首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

    从图的汇点开始反推,可知汇点(第一个败态)的sg值为0。

    性质:

    败态等价于sg值为0。

    游戏和的SG函数等于各个游戏SG函数的Nim和。这样就可以将每一个子游戏分而治之,从而简化了问题。

    类似这样用

    ```cpp

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;

    const int MAXN=1000005;
    const int N=1000005;

    //f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
    //SG[]:0~n的SG函数值
    //S[]:为x后继状态的集合
    vector<int> nextofSG[MAXN];
    int f[N],SG[MAXN],S[MAXN];
    void getSG(int n){
    for(int i = 1; i <= n; i++){
    int l=nextofSG[i].size();
    //后继状态 最多有l 种
    for(int j=0;j<=l;j++){
    S[j]=0;
    }
    for(auto vi:nextofSG[i]){
    //vi:从i状态能取走的石子数
    S[SG[i-vi]]=1;
    }
    for(int j=0;j<=l;j++){
    if(!S[j]){
    SG[i] = j;
    break;
    }
    }
    cout<<"SG["<<i<<"]="<<SG[i]<<endl;
    }
    }

    int NNN=100;

    void enque(int id){
    int B=4;
    int cur=1;
    while(id+cur<=NNN){
    nextofSG[id+cur].push_back(id);
    cur*=B;
    if(B==1)
    break;
    }
    }

    int main() {
    #ifdef Yinku
    freopen("Yinku.in", "r", stdin);
    #endif // Yinku
    /*while(~scanf("%d%d",&b,&n)){
    if(n==1||ispow(n,b))
    fi();
    else{

    }
    }*/
    for(int i=0;i<=NNN;++i){
    enque(i);
    }
    getSG(NNN);
    }

    ```

  • 相关阅读:
    C语言练习之计算某年日是该年的第几天
    C语言练习之 猴子吃桃问题
    C语言练习之 求阶乘
    C语言学习(四)
    C语言学习(三)
    C语言学习(二)
    C语言学习(一)
    自定义函数汇总
    #2019121200026 最大子序列和
    #2019121000025-LGTD
  • 原文地址:https://www.cnblogs.com/Yinku/p/10328425.html
Copyright © 2011-2022 走看看