boj 65

Description

    champ最近在和dalong玩一个取石子游戏,游戏规则很简单:有三堆石子,两人轮流取,每次任选两堆石子,然后从一堆中取走x(x>=1)个石子,另一堆中取走2*x个石子,最后不能取者输掉游戏,champ每一次都先取。
    现在,champ告诉你初始三堆石子的数量,他想知道,自己是否有必胜的策略。你可以假定champ和dalong都足够聪明,每次都会选择最优的策略。



Input
多组测试数据
每行3个正整数(范围[0,255]),表示最初三堆石子的数量。
0 0 0表示输入结束。这组数据不用处理


Output
每组输出一行
如果champ有必胜策略,那么输出"champ",否则输出"dalong"。(注意,引号不要输出)


Sample Input

1 2 0
1 4 1
0 0 0



Sample Output

champ
dalong



Source
champ

 

 

 

解题:

博弈论问题,规定P状态为后手胜状态,N状态为先手胜状态,对于(0,0,0)为终止状态,当处于终止状态时,先手负,终止状态为P状态。如果对于某一状态(a,b,c)可以按照游戏规则转换为P状态,则当前状态为N状态。而对于P状态,任何符合操作的变换只能到达N状态。

对于此题,通过计算sg[x][y][z](表示某个状态(x,y,z)的sg值)来判断当前状态为P或N状态。

开始的时候,是从上到下构造,计算sg[i][j][k],结果超时,超时代码:

#include<iostream>
using namespace std;

bool sg[256][256][256];

void exchange(int &i,int &j,int &k)
{
	if(k<j)swap(k,j);
	if(j<i)swap(i,j);
	if(k<j)swap(k,j);
}
int main()
{
	for(int i=0;i<256;i++)
	{
		for(int j=i;j<256;j++)
		{
			for(int k=j;k<256;k++)
			{
				if(j==0)
					sg[i][j][k]=false;//P态,先手赢
				else
				{
					int tmp[3];
					bool flag=false;
					for(int r=1;r<=k;r++)
					{
						for(int p=0;p<3;p++)
						{
							for(int q=p+1;q<3;q++)
							{
								tmp[0]=i;
								tmp[1]=j;
								tmp[2]=k;
								if(tmp[p]>=r&&tmp[q]>=2*r)
								{
									tmp[p]-=r;
									tmp[q]-=2*r;
									exchange(tmp[0],tmp[1],tmp[2]);
									if(sg[tmp[0]][tmp[1]][tmp[2]]==false)
									{
										flag=true;
										break;
									}
								}
								tmp[0]=i;
								tmp[1]=j;
								tmp[2]=k;
								if(tmp[p]>=2*r&&tmp[q]>=r)
								{
									tmp[p]-=2*r;
									tmp[q]-=r;
									exchange(tmp[0],tmp[1],tmp[2]);
									if(sg[tmp[0]][tmp[1]][tmp[2]]==false)
									{
										flag=true;
										break;
									}
								}
							}
							if(flag)
								break;
						}
						if(flag)
							break;
					}
					sg[i][j][k]=flag;
				}
			}
		}
	}
	int a,b,c;
	scanf("%d%d%d",&a,&b,&c);
	while(!(a==0&&b==0&&c==0))
	{
		exchange(a,b,c);
		if(sg[a][b][c])
			printf("champ\n");
		else
			printf("dalong\n");
		scanf("%d%d%d",&a,&b,&c);
	}
}

 

 主要是,对于每个sg[i][j][k],需要计算能否走到P状态,这样从上到下计算,复杂度,很高。可以从下到上计算,对于P状态sg[i][j][k],通过允许操作能到达的sg[i+n][j+2n][k]...等状态均为N状态,其他不能到达的状态即为P状态,简化了计算。AC代码如下:

#include<iostream>
using namespace std;

bool sg[256][256][256];

void exchange(int &i,int &j,int &k)
{
	if(k<j)swap(k,j);
	if(j<i)swap(i,j);
	if(k<j)swap(k,j);
}
int main()
{
	for(int i=0;i<256;i++)
		for(int j=i;j<256;j++)
			for(int k=j;k<256;k++)
				sg[i][j][k]=false;
	for(int i=0;i<256;i++)
	{
		for(int j=i;j<256;j++)
		{
			for(int k=j;k<256;k++)
			{
				if(sg[i][j][k]==false)
				{
					int tmp[3];
					for(int p=0;p<3;p++)
						for(int q=p+1;q<3;q++)
						{
							tmp[0]=i;
							tmp[1]=j;
							tmp[2]=k;
							for(int t=1;tmp[p]+t<256&&tmp[q]+2*t<256;t++)
							{
								tmp[p]+=t;
								tmp[q]+=2*t;
								exchange(tmp[0],tmp[1],tmp[2]);
								sg[tmp[0]][tmp[1]][tmp[2]]=true;
								tmp[0]=i;
								tmp[1]=j;
								tmp[2]=k;
							}
							for(int t=1;tmp[p]+2*t<256&&tmp[q]+t<256;t++)
							{
								tmp[p]+=2*t;
								tmp[q]+=t;
								exchange(tmp[0],tmp[1],tmp[2]);
								sg[tmp[0]][tmp[1]][tmp[2]]=true;
								tmp[0]=i;
								tmp[1]=j;
								tmp[2]=k;
							}
						}
				}
			}
		}
	}
	int a,b,c;
	scanf("%d%d%d",&a,&b,&c);
	while(!(a==0&&b==0&&c==0))
	{
		exchange(a,b,c);
		if(sg[a][b][c])
			printf("champ\n");
		else
			printf("dalong\n");
		scanf("%d%d%d",&a,&b,&c);
	}
}

 

你可能感兴趣的:(BO)