题目链接:http://lightoj.com/volume_showproblem.php?problem=1401
题意:一个字符串两个轮流在空闲的位置放置字母X或O,不允许出现两个连续的X或者两个连续的O。谁不能放谁输。
思路:记SG[n][a][b]为长为n的空串左右两侧的限制为a和b,0表示无限制,1表示这一侧不能放X,2表示这一侧不能放O。
const int INF=2000000000;
const int N=105;
int SG[N][3][3],C,num=0,n;
string s;
int OK(int i,char c,string s,int a,int b)
{
int L=0,R=Len(s)-1;
if(i==L)
{
if(c=='X'&&a==1||c=='O'&&a==2) return 0;
if(R>L)
{
if(s[i+1]==c) return 0;
return 1;
}
else
{
if(c=='X'&&b==1||c=='O'&&b==2) return 0;
return 1;
}
}
else if(i==R)
{
if(c=='X'&&b==1||c=='O'&&b==2) return 0;
if(R>L)
{
if(s[i-1]==c) return 0;
return 1;
}
else
{
if(c=='X'&&a==1||c=='O'&&a==2) return 0;
return 1;
}
}
else
{
if(c==s[i-1]||c==s[i+1]) return 0;
return 1;
}
}
int DFS(string,int,int);
int getSG1(string s,int a,int b)
{
int n=Len(s);
if(SG[n][a][b]!=-1) return SG[n][a][b];
return SG[n][a][b]=DFS(s,a,b);
}
int getSG(string s,int a,int b)
{
int ans=0,L=a,R,i,n=Len(s);
string s1="";
for(i=0;i<=n;i++)
{
if(i<n&&s[i]=='.') s1+=s[i];
else
{
if(Len(s1)==0)
{
if(i==n);
else if(s[i]=='X') L=1;
else L=2;
continue;
}
if(i==n) R=b;
else if(s[i]=='X') R=1;
else R=2;
ans^=getSG1(s1,L,R);
s1="";
L=R;
}
}
return ans;
}
int DFS(string s,int a,int b)
{
int p[300]={0},i,n=Len(s);
FOR0(i,n) if(s[i]!='X'&&s[i]!='O')
{
if(OK(i,'X',s,a,b))
{
s[i]='X';
p[getSG(s,a,b)]=1;
s[i]='.';
}
if(OK(i,'O',s,a,b))
{
s[i]='O';
p[getSG(s,a,b)]=1;
s[i]='.';
}
}
for(i=0;p[i];i++);
return i;
}
int main()
{
clr(SG,-1);
RD(C);
while(C--)
{
RD(s);
n=Len(s);
int i,cnt=0;
FOR0(i,n) if(s[i]!='.') cnt++;
int p=DFS(s,0,0);
printf("Case %d: ",++num);
if(cnt%2&&p||cnt%2==0&&!p) puts("No");
else puts("Yes");
}
return 0;
}