Vision_MATH_SG函数

来源:互联网 发布:棋盘分割算法优缺点 编辑:程序博客网 时间:2024/05/22 05:04
///定义:
/*
    概念:给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,
无法移 动者判负。事实上,这个游戏可以认为是所有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的子结点 }。
*/

原创粉丝点击