博弈 取石子
题目链接:http://acm.bupt.edu.cn/onlinejudge/newoj/showProblem/show_problem.php?problem_id=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
这个题用博弈来解决,我们用数组sg[x][y][z]来表示这个状态是P或者N,如果我们直接搜索博弈树,根据PN规则,从上到下搜索所有的子局面,
根据他们的PN得出现在的状态,时间复杂度为(n^4),由于组成的有向图是无环的(因为每次都是减少的),所以我们从下往上扩展。
对于一个状态a,b,c若它是P,则扩展的到任何可能的状态都是N,这样负责度为(n^3+ 失败点个数*n).
代码如下:
#include <iostream>
#include <stdio.h>
using namespace std;
const int N=256;
bool sg[N][N][N];
int x,y,z;
void exchange(int &a,int &b,int &c)
{
int tmp;
if(a>b)
{
tmp=b;
b=a;
a=tmp;
}
if(b>c)
{
tmp=c;
c=b;
b=tmp;
}
if(a>b)
{
tmp=b;
b=a;
a=tmp;
}
}
void init()
{
int i,j,k,t,a,b,c;
for(i=0;i<N;i++)
for(j=0;j<N;j++)
for(k=0;k<N;k++)
sg[i][j][k]=0;
for(i=0;i<N;i++)
{
for(j=i;j<N;j++)
{
for(k=j;k<N;k++)
{
if(!sg[i][j][k])
{
//扩展i,j
for(t=1;(i+t<N && j+2*t<N);t++)
{
a=i+t;
b=j+2*t;
c=k;
exchange(a,b,c);
sg[a][b][c]=1;
}
for(t=1;(i+2*t<N && j+t<N);t++)
{
a=i+2*t;
b=j+t;
c=k;
exchange(a,b,c);
sg[a][b][c]=1;
}
//扩展i,k
for(t=1;(i+t<N && k+2*t<N);t++)
{
a=i+t;
b=j;
c=k+2*t;
exchange(a,b,c);
sg[a][b][c]=1;
}
for(t=1;(i+2*t<N && k+t<N);t++)
{
a=i+2*t;
b=j;
c=k+t;
exchange(a,b,c);
sg[a][b][c]=1;
}
//扩展j,k
for(t=1;(j+t<N && k+2*t<N);t++)
{
a=i;
b=j+t;
c=k+2*t;
exchange(a,b,c);
sg[a][b][c]=1;
}
for(t=1;(j+2*t<N && k+t<N);t++)
{
a=i;
b=j+2*t;
c=k+t;
exchange(a,b,c);
sg[a][b][c]=1;
}
}
}
}
}
}
int main()
{
init();
while(1)
{
scanf("%d%d%d",&x,&y,&z);
if(!x && !y && !z)
break;
exchange(x,y,z);
if(sg[x][y][z])
printf("champ\n");
else
printf("dalong\n");
}
return 0;
}