Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3352 Accepted Submission(s): 2099
#include<stdio.h> int main() { int k,n; while(scanf("%d",&n)>0) { k=n%3; if(k==0) printf("Cici\n"); else printf("Kiki\n"); } return 0; }
SG博弈求解的王道。
关键是对SG的求解,直接吧所有的情况打表。sg的值为0的时候,奇异状态,输,否则赢。
给出几个例子说明。贴别人牛人的博客。
Ø g(0)={},G(0)={0, 1, …},f(0)=0;
Ø g(1)={},G(1)={0, 1, …},f(1)=0;
Ø g(2)={#(0)}={f(0)}={0},G(2)={1, 2, …},f(2)=1;
Ø g(3)={#(1)}={f(1)}={0},G(2)={1, 2, …},f(3)=1;
Ø g(4)={#(2), #(1, 1)}={f(2), f(1)+f(1)}={1, 0},G(4)={2, 3, …},f(4)=2;
Ø g(5)={#(3), #(1, 2)}={f(3), f(1)+f(2)}={1, 1},G(5)={0, 2, 3, …},f(5)=0;
Ø g(6)={#(4), #(1, 4), #(2, 2)}={2, 1, 0},G(6)={3, 4, …},f(6)=3;
Ø g(7)={#(4), #(1, 4), #(2, 3)}={2, 2, 0},G(7)={1, 3, 4, …},f(7)=1;
G 图2所示的局面S=(7, 3, 3),有#S=f(7)+f(3)+f(3)=1+1+1=1,故S胜。
G 游戏C的初始局面S=(3, 4, 6),有#S=1+2+3=01+10+11=0,故S负。
步骤:
2 此类搏弈游戏的一般性解法:
F 用一个n元组(a1, a2, …, an),来描述游戏过程中的一个局面。
F 用符号#S,表示局面S所对应的二进制数。
F 用符号$(x),表示局面(x)的下一步所有可能出现的局面的集合。
F 定义集合g(x):设$(x)={S1, S2, …, Sk},则g(x)={#S1, #S2, …, #Sk}。
F 令非负整数集为全集,集合G(x)表示集合g(x)的补集。
F 定义函数f(n):f(n)=min{G(n)},即f(n)等于集合G(n)中的最小数。
F 设局面S=(a1, a2, …, an),#S=f(a1)+f(a2)+…+f(an),采用二进制数的加法。
感觉有些繁琐,结合代码分析一下
{
1.首先我们要求出下一个局面可能出现的情况,
2.对应的SG值进行用数组标记.
3.找出最小的值。
}
代码:
#include<stdio.h> #include<stdlib.h> #include<string.h> int hash[1001],SG[1001]; int arry[12]={12,1,2,4,8,16,32,64,128,256,512,1024}; void getsg() { int tmp,i,j; for(i=0;i<=1000;i++) { memset(hash,0,sizeof(hash[0]));//对每一个sg[i]的值,都要用到 for(j=1;j<arry[0];j++) { if(arry[j]>i) //判断是否是下一个局面 break; tmp=i-arry[j]; hash[SG[tmp]]=1; //对应的g(5)={#(3), #(1, 2)}={f(3),f(1)+f(2)}={1, 1},f(5)=0; //f(n)就是SG[i]. } for(j=0;;j++) // 找出最小的。 if(hash[j]==0) { SG[i]=j; //此次不能认为可以是SG[i]=1;因为上面hash[SG[tmp]]=1要用到。 break; } } } int main() { int k,n; memset(SG,0,sizeof(SG[0])); getsg(); while(scanf("%d",&n)>0) { k=SG[n]; if(k==0) printf("Cici\n"); else printf("Kiki\n"); } return 0; }