[UVa 10561]Treblecross游戏

 Treblecross游戏

★★   输入文件: treblecross.in   输出文件: treblecross.out    简单对比
时间限制:1 s   内存限制:256 MB

【题目描述】

Treblecross游戏是一种双人游戏,游戏在一个一行,n列的棋盘上进行。初始时所有的格子都是空的。两名玩家轮流向某个空格子中放置一个X。如果此时有三个连续的X出现,则该玩家获胜。

给出一个游戏的中间状态,计算先手是否必胜(假设两名玩家都执行最佳策略)。如果先手必胜,那么输出这一步的所有必胜策略。

考虑一个在1行5列棋盘上的Treblecross游戏。如果第一名玩家把X放在第三个(即中间的)格子中,那么状态变成了 ..x.. ,无论另一名玩家如何操作,他都能获胜。但如果第一名玩家把X放在其他任何位置,另一名玩家都可以通过把X放在棋盘另一边获胜(例如,在第二名玩家操作后状态可能变成.X..X)。在这种情况下,无论第一名玩家下一步进行什么操作,第二名玩家都能获胜。

【输入格式】

输入包含多组数据。

输入文件的第一行是一个正整数N(N<100),表示数据组数。

每组数据只有一行,是一个由'X'和'.'组成的字符串,其中'.'表示空格子。字符串长度(即棋盘的长度)在[3,200]之间。保证不会有三个X并排。

【输出格式】

对于每组数据,若先手必胜则首先输出"WINNING",否则输出"LOSING"。

然后在第二行,按递增顺序输出先手的所有必胜策略,即先手下一步放X的位置。格子从左到右以1,2,3,...编号。

【样例输入】

4
.....

X.....X..X.............X....X..X

.X.X...X

...............................................

【样例输出】


WINNING

3

LOSING


WINNING

3

WINNING

1 12 15 17 20 24 28 31 33 36 47


【来源】

UVa10561 Treblecross

刘汝佳,《算法竞赛入门经典训练指南》表2.6




#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>


using namespace std;
#define A(p) max(p,0)
int SG[210];
int Mex(int pos){
	bool vis[200]={0};
	for(int i=1;i<=pos;i++)
		vis[SG[A(i-3)]^SG[A(pos-i-2)]]=true;
	for(int i=0;;i++)if(!vis[i])return i;
}
int n,m;
string s;
int z[210];
int pd(int pos){
	if(z[pos-1]==1||z[pos+1]==1||z[pos+2]==1)return true;
	if(pos>2&&z[pos-2]==1)return true;
	return false;
}
int pd1(int pos){
	if(z[pos-1]==1&&z[pos+1]==1)return 1;
	if(pos>2&&z[pos-1]==1&&z[pos-2]==1)return 1;
	if(z[pos+1]==1&&z[pos+2]==1)return 1;
	return 0;
}
int last[200],next[200];
int main(){
	freopen("treblecross.in","r",stdin);
	freopen("treblecross.out","w",stdout);
	for(int i=1;i<=3;i++)SG[i]=1;
	for(int i=4;i<=200;i++)SG[i]=Mex(i);
	scanf("%d",&n);
	while(n--){
		int first=1,ans=0;
		memset(z,-1,sizeof z);
		memset(last,-1,sizeof last);
		memset(next,-1,sizeof next);
		cin>>s;m=s.size();
		for(int i=0;i<m;i++)z[i+1]= s[i]=='X';
		for(int i=1;i<=m;i++)
			last[i]= z[i]==1?i:last[i-1];
		next[m+1]=m+1;
		next[m]= z[m]==1?m:m+1;
		for(int i=m-1;i>=1;i--)
			next[i]= z[i]==1?i:next[i+1];
		int X=0;
		for(int i=1;i<=m;i++){
			if(z[i]==0)continue;
			if(pd(i))goto Mark;
			int pos=last[i-1];X=1;
			if(last[i-1]==-1){ans^=SG[A(i-3)];continue;}
			ans^=SG[A(i-pos-5)];
		}
		if(!z[m]){
			int l=last[m]==-1?0:last[m]+2;
			ans^=SG[m-l];
		}
		if(!X)ans=SG[m];
		if(ans){
			printf("WINNING\n");
			for(int i=1;i<=m;i++){
				if(z[i]==1||pd(i))continue;
				int l=last[i-1]==-1?1:last[i-1]+3;
				int r=next[i+1]==m+1?m:next[i+1]-3;
				if((ans^SG[A(r-l+1)]^SG[A(i-l-2)]^SG[A(r-i-2)])==0)
					if(first){first=0;printf("%d",i);}
					else printf(" %d",i);
			}printf("\n");
		}
		else printf("LOSING\n\n");
		continue;
		Mark:
			printf("WINNING\n");
			for(int i=1;i<=m;i++)if(pd1(i))
				if(first){first=0;printf("%d",i);}
				else printf(" %d",i);
			printf("\n");
	}
	return 0;	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值