Vision_MATH_SG函数

///定义:
/*
    概念:给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,
无法移 动者判负。事实上,这个游戏可以认为是所有Impartial Combinatorial Games的抽象模型。也就是说,
任何一个ICG都可以通过把每个局面看成一个顶点,对每个局面和它的子局面连一条有向边来抽象成这个“有向图游戏”。
下 面我们就在有向无环图的顶点上定义Sprague-Grundy函数。首先定义mex(minimal excludant)运算,这是施加于一个
集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{1,3,5}=0、mex{}=0。
(一)
    1.把原游戏分解成多个独立的子游戏,则原游戏的SG函数值是它的所有子游戏的SG函数值的异或。
即sg(G)=sg(G1)^sg(G2)^...^sg(Gn)。
    2.分别考虑没一个子游戏,计算其SG值SG值的计算方法:(重点)
(二)
    1.可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);
    2.可选步数为任意步,SG(x) = x;
    3.可选步数为一系列不连续的数,用模板计算
(三)
* 必败(必胜)点的属性:
* (1) 所有终结点是必败点(P点);
* (2) 从任何必胜点(N点)操作,至少有一种方法可以进入必败点(P点);
* (3)无论如何操作, 从必败点(P点)都只能进入必胜点(N点).
*如果从某个点开始的所有一步操作都只能进入必胜点(N点) ,则将该点标记为必败点(P点)


*/


///代码:

/*
**name:SG函数
**function:某堆石子一次拿的数量是规定的,只能拿s[]数组中的值的个数(保证s数组时有序的)
**实现:for循环
*/
#include <iostream>
using namespace std;
int sg[N];  
bool hash[N];  
void sg_solve(int *s,int t,int N){//N求解范围 S[]数组是可以每次取的值,t是s的长度。  
    int i,j;  
    memset(sg,0,sizeof(sg));
    for(i=1;i<=N;i++){
        memset(hash,0,sizeof(hash));
        for(j=0;j<t;j++)
            if(i-s[j] >= 0)
            hash[sg[i-s[j]]] = 1;
        for(j=0;j<=N;j++)
            if(!hash[j])
            break;
        sg[i] = j;
   }
}


/*
**name:SG函数
**function:某堆石子一次拿的数量是规定的,只能拿s[]数组中的值的个数(保证s数组时有序的)
**实现:递归
*/
//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
//n是集合s的大小 S[i]是定义的特殊取法规则的数组
int s[110],sg[10010],n;
int SG_dfs(int x){
    int i;
    if(sg[x]!=-1)
       return sg[x];
    bool vis[110];
    memset(vis,0,sizeof(vis));
    for(i=0;i<n;i++){
            if(x>=s[i]){
                SG_dfs(x-s[i]);
                vis[sg[x-s[i]]]=1;
            }
    }
    int e;
    for(i=0;;i++)
        if(!vis[i]){
             e=i;
            break;
        }
    return sg[x]=e;
}

/*
**name:SG函数
**function:二维SG函数,nim扩展到二维
**实现:for循环
*/
int sg[maxn+7][maxn+7],vis[maxn*maxn+7];
int Getsg(int x,int y){
    if(sg[x][y]!=-1)return sg[x][y];
    memset(vis,0,sizeof(vis));
    for(int i = 0;i<x;i++){
        for(int j = 0;j<y;j++){
            vis[sg[x][j]^sg[i][y]] = 1;
        }
    }
    for(int i = 0;;i++){
        if(!vis[i])return i;
    }
}
void init(){
    memset(sg,-1,sizeof(sg));
    for(int i = 0;i<=maxn;i++)sg[i][0] = sg[0][i] = i;
    for(int i = 0;i<maxn;i++){
        for(int j = 0;j<maxn;j++){
            sg[i][j]=Getsg(i,j);
        }
    }
}

///扩展:
/*
扩展:
分析方向:
    (1)从必败点开始入手找其父节点的状态即SG值
    (2)从数的奇偶性入手分析
    (3)如果顺序的拿就是普通的求SG值的方法,如果不是顺序的或乱序的就对SG值进行更改。
    (4)其实SG的本质就是从根节点开始求其父节点SG值的过程
    (5)二维SG模板如上
    (6)一般NIM游戏是谁拿最后的石子必胜,直接异或就行,如果求谁拿最后的石子为失败的话就单独判断都是1的时
候的奇偶性就行了,其他的也是看其异或值
    (7)题意:树上的删边游戏,删掉一条边后只保留与根节点向连的部分,最后无法删边的人输.
Colon原理:SG(x)=XOR{ SG(y)+1|y是x的子结点 }。
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值