Game
2016-03-16 19:37
316 查看
题目描述
两个人玩一个游戏。有一个1*n的棋盘,有一些格子已经染了色。两个人轮流操作,每次一个人可以选择一个没有染色的格子,把它染成白色或黑色,要求相邻的格子不能染成相同的颜色。最后不能操作的那个人为输。给出棋盘的初始状况,并且假设两个人都采取最优策略,问谁会赢?
SG分析
我们设:sg[0,n]表示现在有连续n个未涂色格子,其两端的格子是不同颜色的估价函数值。
sg[1,n]表示现在有连续n个未涂色格子,其两端的格子是相同颜色的估价函数值。
sg[2,n]表示现在有连续n个未涂色格子,其有一端是格子另一端是边界。
sg[3,n]表示现在有连续n个未涂色格子,其两端的格子都是边界。
举个例子
10002
那么这归属状态sg[0,3]
10001
那么这归属状态sg[1,3]
0001
那么这归属状态sg[2,3]
000
那么这归属状态sg[3,3]
现在我们来推一推它们的转移,懒得写了详见代码
然后打个表,我们可以发现结论
#include<cstdio> #include<iostream> #include<algorithm> #define fo(i,a,b) for(i=a;i<=b;i++) using namespace std; const int maxn=1000+10; int sg[4][maxn];//0 diff 1 same 2 free1 3 free2 bool bz[maxn]; int i,j,k,l,t,n,m; int main(){ n=1000; sg[0][1]=0; sg[1][1]=1; sg[2][1]=1; sg[3][1]=1; fo(i,2,n){ bz[sg[1][i-1]]=1; fo(j,1,i-2){ k=i-j-1; bz[sg[0][j]^sg[1][k]]=1; } fo(j,0,n+1) if (!bz[j]) break; sg[0][i]=j; fill(bz,bz+n+2,0); bz[sg[0][i-1]]=1; fo(j,1,(i-1)/2){ k=i-j-1; bz[sg[0][j]^sg[0][k]]=1; bz[sg[1][j]^sg[1][k]]=1; } fo(j,0,n+1) if (!bz[j]) break; sg[1][i]=j; fill(bz,bz+n+2,0); bz[sg[2][i-1]]=1; bz[sg[1][i-1]]=1; bz[sg[0][i-1]]=1; fo(j,1,i-2){ k=i-j-1; bz[sg[0][k]^sg[2][j]]=1; bz[sg[1][k]^sg[2][j]]=1; } fo(j,0,n+1) if (!bz[j]) break; sg[2][i]=j; fill(bz,bz+n+2,0); bz[sg[2][i-1]]=1; fo(j,1,i-2){ k=i-j-1; bz[sg[2][j]^sg[2][k]]=1; } fo(j,0,n+1) if (!bz[j]) break; sg[3][i]=j; fill(bz,bz+n+2,0); } //fo(i,1,20) printf("sg[0][%d]:%d\n",i,sg[0][i]); //fo(i,1,20) printf("sg[1][%d]:%d\n",i,sg[1][i]); //fo(i,1,20) printf("sg[2][%d]:%d\n",i,sg[2][i]); //fo(i,1,20) printf("sg[3][%d]:%d\n",i,sg[3][i]); }
结论
sg[0,i]=0sg[1,i]=1
sg[2,i]=i
sg[3,i]=i%2
这是通过打表发现的,而事实证明很多博弈论SG函数的题目的结论都是推理总结打表找规律,可以节约时间,所以这里不予证明(其实证明并不难)。
接下来我们有了sg函数,就可以求了。
#include<cstdio> #include<algorithm> #define fo(i,a,b) for(i=a;i<=b;i++) #define fd(i,a,b) for(i=a;i>=b;i--) using namespace std; int i,j,k,l,t,n,m,ca; bool czy; char s[100000+10],ch; int main(){ freopen("game.in","r",stdin); while (scanf("%d",&n)!=EOF){ do{ s[1]=getchar(); }while (s[1]<'0'||s[1]>'2'); fo(i,2,n) scanf("%c",&s[i]); czy=0; fo(i,1,n){ if (s[i]!='0'){ czy=1; break; } } if (!czy){ if (n%2) printf("FIRST\n");else printf("SECOND\n"); } else{ fo(i,1,n) if (s[i]!='0') break; j=i; fd(i,n,1) if (s[i]!='0') break; k=i; t=(j-1)^(n-k); fo(i,j+1,k){ if (s[i]!='0'){ if (i-j>1&&s[j]==s[i]) t^=1; j=i; } } if (t) printf("FIRST\n");else printf("SECOND\n"); } } }