一、POJ 1067 取石子游戏
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
威佐夫博弈(Wythoff Game)
用一个二维数组来表示玩家将面临的局面,即a[i][j]表示两堆石头分别有i个和j个,如果a[i][j]是必败局面的话,那么——这个点的右侧所有点a[i][k](k>j)都是必胜点,因为可以通过拿走第二堆的k-j个石子令对方面临必败局面a[i][j];这个点的下侧所有点a[k][j](k>i)都是必胜点,因为可以通过拿走第二堆的k-i个石子令对方面临必败局面a[i][j];这个点的右下方45度所有点a[i+k][j+k](k>0)都是必胜点,因为可以通过同时拿走两堆的k个石子令对方面临必败局面a[i][j]。这样如果在二维数组a[i][j]处标记”X”表示必败的话,在上面提到的三类点的位置都可以标记”O”表示必胜了,做完这项工作后,再挑选如今距离原点最近的未被标记的点,它一定是下一个必败点——因为它无法通过游戏规则移动到一个必败点,并且规则规定的动作都是朝向原点移动的,而它是距离原点最近的未被标记的点,因此它只能移动到一个必胜点从而让对方获胜,所以该点一定是下一个必败点。有了新必败点后就可以重复上述工作,直到找出问题范围内的所有必败点。
前几个必败点如下:(0,0),(1,2),(3,5),(4,7),(6,10),(8,13)……可以发现,对于第k个必败点(m(k),n(k))来说,m(k)是前面没有出现过的最小自然数,n(k)=m(k)+k。
m(k) = k * (1 + sqrt(5))/2
n(k) = m(k) + k
int main()
{
int n,m;
while (cin>>n>>m)
{
if (n>m) swap(n,m);
double x=(1+sqrt(5))*0.5;
if (floor((m-n)*x)==n)puts("0");else puts("1");
}
}
二、POJ 1740 A New Stone Game
int main()
{
while (~scanf("%d",&n) && n)
{
for (i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+n+1);
if (n&1)puts("1");
else
{
m=0;
for (i=2;i<=n;i+=2)
{
if (a[i]!=a[i-1])m=1;
}
if (!m)puts("0");else puts("1");
}
}
return 0;
}
int main()
{
while (~scanf("%d",&n) && n)
{
t=0;
for (i=1;i<=n;i++)scanf("%d",&a[i]),t=t^a[i];
if (t)puts("Yes");else puts("No");
}
return 0;
}
int main()
{
int n,m;
while (cin>>n>>m && n)
{
if (nm)break;
int t=n-m;n=m;m=t;
num++;
}
if (num&1)puts("Ollie wins");else puts("Stan wins");
}
}
return 0;
}
void dfs(int x)
{
int len,i,j,k,n;
n=x;len=0;
while (n) { len++; n/=10; }
for (i=1;i<=len;i++)
{
int b=pow(10,i-1);
j=x % (b*10) / b;
for (k=1;k<=9-j;k++)
if (x+k*b
int i,j,k,l,n,m,t,h;
vector G[N];
bool f[N];
int sg[N];
int dfs(int x)
{
if (sg[x]!=-1)return sg[x];
bool c[30];
memset(c,0,sizeof c);
int i;
for (i=0;i
[n/9/2,n/9-1]为必败点,因为他们只能到达必胜点;
“/”为向上取整
代码:
int main()
{
int n,x;
while(~scanf("%d",&n))
{
for(x=0;n>1;x++)
{
if(x&1)
n = ceil(n*1.0/2);
else
n = ceil(n*1.0/9);
}
puts(x&1?"Stan wins.":"Ollie wins.");
}
return 0;
}
八、HDU 1536 S-Nim
题意:
告诉一个集合,每次取石子只能取集合中的个数,多个询问,告诉每堆石子的个数,问先手是否必胜。
题解:
SG函数。预处理出来所有可能堆数的SG函数,询问时直接异或。
代码:
int sg[N];
int i,j,k,l,n,m,t,h,a[110];
int dfs(int x)
{
if (sg[x]!=-1)return sg[x];
bool c[101]={0};
for (int i=1;i<=n;i++)
{
int t=x-a[i];
if (t<0)break;
sg[t]=dfs(t);
c[sg[t]]=1;
}
for (int i=0;;i++)if (!c[i])return i;
}
int main()
{
while (~scanf("%d",&n) && n)
{
memset(sg,-1,sizeof sg);
for (i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
for (i=0;i
九、HDU 1536
从一个n*n的角落出发,每次移动到相邻的,而且没有经过的格子上。谁不能操作了谁输。
结论就是n为偶数,先手赢,奇数,后手赢。
S表示起点。如果n为偶数,那么所有格子可以被2*1的砖块覆盖掉。这样先手每次都移动到当前1*2的另外一块。先手必赢。
如果n为奇数。出了起始那个点,其余点都可以被覆盖。所以后手赢。
代码:
int main()
{
int n;
while (~scanf("%d",&n) && n)
{
if (n&1)puts("ailyanlu");else puts("8600");
}
return 0;
}
十、HDU 1729 Stone Game
题意:
有n个箱子,每个箱子有一个容量,两人轮流想箱子里放石子,每次放的个数不能超过那个箱子里已经有的石子的个数。问先手必胜还是必败。
题解:
首先,约定(a,b)表示容量为a的箱子,当前有b个石子在里面。
求必败态。
首先(S,S)为必败态。设p=max{x | x*x+x
下一个必败态:(p,t),其中t=max{x | x*x+x
下一个必败态:(t,k),其中k=max{x | x*x+x
.......
用递归求解,每一个必败态(a,b),如果c==b,必败,如果c>b,则SG值为a-c,如果c
代码:
int dfs(int a,int b)
{
int t=sqrt(a);
while (t+t*t>=a)t--;
if (b>t)return a-b;else return dfs(t,b);
}
int main()
{
int n,tt=0;
while (~scanf("%d",&n) && n)
{
int ans=0;
for (int i=1;i<=n;i++)
{
int j,k;
scanf("%d%d",&j,&k);
ans=ans^dfs(j,k);
}
if (ans)printf("Case %d:\nYes\n",++tt);
else printf("Case %d:\nNo\n",++tt);
}
return 0;
}