博弈论(2)
甲乙两人面对若干排石子,其中每一排石子的数目可以任意确定。例如图所示的初始局面:共n=3排,其中第一排的石子数a1=7,第二排石子数a2=3,第三排石子数a3=3。两人轮流按下列规则取走一些石子,游戏的规则如下:每一步必须从某一排中取走两枚石子;这两枚石子必须是紧紧挨着的;如果谁无法按规则取子,谁就是输家。
解:
用符号#S,表示局面S所对应的二进制数。
用符号$(x),表示局面(x)的下一步所有可能出现的局面的集合。
定义集合g(x):设$(x)={S1, S2, …, Sk},则g(x)={#S1, #S2, …, #Sk}。
函数f满足要求的一个充分条件
f(a1)不属于集合g(a1)。
集合g(a1)包含集合{0, 1, …, f(a1)–1}。
如果g(a1)={0, 1, 2, 5, 7, 8, 9},则f(a1)=3,满足要求。
用大写字母N表示非负整数集,即N={0, 1, 2, …}。
令N为全集,集合G(x)表示集合g(x)的补集。
定义函数f(n):f(n)=min{G(n)},即f(n)等于集合G(n)中的最小数。
设局面S=(a1, a2, …, an),#S=f(a1)+f(a2)+…+f(an),采用二进制数的加法。
若#S=0,则S负;若#S≠0,则S胜。
游戏C的f值:
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, 3), #(2, 2)}={2, 1, 0},G(6)={3, 4, …},f(6)=3;
g(7)={#(5), #(1, 4), #(2, 3)}={0, 2},G(7)={1, 3, 4, …},f(7)=1;
图2所示的局面S=(7, 3, 3),有#S=f(7)+f(3)+f(3)=1+1+1=1,故S胜。
游戏C的初始局面S=(3, 4, 6),有#S=1+2+3=01+10+11=0,故S负。
#include<iostream> using namespace std; #include<stdlib.h> int cmp(const void *a,const void *b) { return *(int *)a-*(int *)b; } int f[1000]={0,0,1,1};//初始化前4个 int g[500],c[1000]; int main() { int i,j,k,m,n,s; for(i=4;i<=1000;i++) { m=0; for(j=1;j<=i/2;j++) { g[m++]=f[j-1]^f[i-j-1]; } qsort(g,m,sizeof(g[0]),cmp);//排序 for(k=0;;k++) if(g[k]!=k)//找其补集的最小值 { f[i]=k;//找到f[i] break; } //printf("a[%d]=%d\n",i,a[i]); } while(scanf("%d",&n)!=EOF&&n) { for(i=0;i<n;i++) cin>>c[i]; s=0; for(i=0;i<n;i++) s=s^f[c[i]]; if(s==0)printf("先取者输\n"); else printf("先取者赢\n"); } return 0; }