您的位置:首页 > 产品设计 > UI/UE

sg(Sprague-Grundy)函数 小结

2015-07-10 20:21 597 查看
sg(Sprague-Grundy)函数

sg值:一个点的SG值就是一个不等于它的后继点的SG的且大于等于零的最小整数。

后继点:也就是按照题目要求的走法,能够走一步达到的那个点。

sg函数值的性质:

sg(x)==0 必败点

sg(x)>0 必胜点

求单个sg函数值

int sg[MAXN];
int get_sg(int x, int n){
if(sg[x]!=-1)
return sg[x];
bool vis[105]={0};
for(int i=0;i<n;++i){
int tmp = a[i]; //tmp是转移的下一个状态的步数
if(x >= tmp){
sg[x - tmp] = get_sg(x - tmp, n);
vis[sg[x - tmp]] = 1;
}
}
for(int i=0;;++i)
if(!vis[i]) return sg[x]=i;
}
void init(){
memset(sg,-1,sizeof(sg));
sg[0]=0;
}


预处理sg值

void get_sg(){
sg[0]=0;
for(int i=1;i<N;++i){
set<int> s;
for(int j=0;j<10;++j){
int tmp=(1<<j); //tmp是转移的下一个状态的步数
if(i >= tmp)
s.insert(sg[i - tmp]);
}
int g=0;
while(s.count(g)!=0) ++g;
sg[i]=g;
}
}


例子:

hdu 1847

题意:

1. 总共n张牌;

2. 双方轮流抓牌;

3. 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)

4. 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;

假设Kiki和Cici都是足够聪明,并且每次都是Kiki先抓牌,请问谁能赢呢?

限制:

1 <= n <= 1000

思路:

sg函数

/*hdu 1847
题意:
1. 总共n张牌;
2. 双方轮流抓牌;
3. 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4. 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明,并且每次都是Kiki先抓牌,请问谁能赢呢?
限制:
1 <= n <= 1000
思路:
sg函数
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
using namespace std;
const int N=1005;
const int M=105;
int sg
;

/*int get_sg(int x){
if(sg[x]!=-1)
return sg[x];
bool vis[M];
memset(vis,0,sizeof(vis));

for(int i=0;i<10;++i){
int tmp = (1<<i);
if(x >= tmp){
sg[x - tmp] = get_sg(x - tmp);
vis[sg[x - tmp]] = 1;
}
}
for(int i=0;;++i)
if(!vis[i]) return i;
}
void get_sg(){
sg[0]=0;
for(int i=1;i<=1000;++i){
sg[i]=get_sg(i);
}
}*/

void get_sg(){
sg[0]=0;
for(int i=1;i<N;++i){
set<int> s;
for(int j=0;j<10;++j){
int tmp=(1<<j);
if(i >= tmp)
s.insert(sg[i - tmp]);
}
int g=0;
while(s.count(g)!=0) ++g;
sg[i]=g;
}
}

void init(){
memset(sg,-1,sizeof(sg));
}

int main(){
init();

get_sg();

int n;
while(scanf("%d",&n)!=EOF){
if(sg
==0)
puts("Cici");
else
puts("Kiki");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: