【博弈】UVA10561 Treblecross

这题细节还挺多的。

分析

拿到题目先拿样例找一下性质。

对于最初的局面,发现如果有一个位置出现 X ,那么它的左边两个或者右边两个位置只要存在 X ,那么先手必然胜利。

如果不存在上面的情况,我们就可以枚举一下先手决策的位置,看看所得到的状态能不能让后手必输。

这时候我们想到用 sg 函数来刻画这一点。

方便起见,我们约定 X 的左右两个位置为“禁着点”(记为 O)(因为谁在这个地方下了 X 谁就输了)。

f[x] 表示空棋盘(也就是 x. )的 sg 函数值。

我们以 x=10 为例(..........):

当下了一个 X 时在位置 p=5 时,棋盘变为 ..OOXOO...

剩下的部分可以看成两个棋盘,大小分别为 2,3


一般地,对于落点 p ,得到的两个新棋盘的 sg 函数值为 f[max(0,p3)], f[max(0,xp2)]

类似地枚举落点的位置,我们可以得到 f[x]

f[x]=mexp=1x(f[max(0,p3)]f[max(0,xp2)])

mex(minimal exdudant)函数

S 表示一个非负整数集合。定义 mex(S) 为求出不属于集合 S 的最小非负整数的运算。
如:{ 0,1,3 } 对应的 mex 值 就是 2


最后还要提醒一下输出问题:

  • 每一行输出行末不要空格!
  • 如果判断得到的是 LOSING ,要额外输出一个空行。

高清程序

#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define debug(x) cerr << #x << ": " << x << endl
#define pb(a) push_back(a)
#define set0(a) memset(a,0,sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define ceil(a,b) (a+(b-1))/b
#define INF 0x3f3f3f3f
#define ll_INF 0x7f7f7f7f7f7f7f7f
typedef long long ll;
typedef pair<int,int> PII;
typedef pair<double,double> PDD;

inline void read(int &x) {
    int s=0;x=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=220;

int n;
string str, tmp;

vector<int> res;

int f[N];

int sg(int x){
	if(~f[x]) return f[x];
	
	unordered_set<int> buf;
	rep(i,1,x){ // 枚举决策求 sg 函数
		int len1=i-3, len2=x-i-2;
		len1=max(len1, 0), len2=max(len2, 0);
		int t=sg(len1)^sg(len2);
		buf.insert(t);
	}
	
	int p=0;
	while(buf.count(p)) p++;
	return f[x]=p;
}

void init(){
	memset(f, -1, sizeof f);
	f[0]=0;
	f[1]=f[2]=f[3]=1;
	
	rep(i,1,N-1) sg(i);
}

int main(){
	init(); // 预处理出 sg 函数并存在 f[] 中
	
	int T; cin>>T;
	while(T--){
		res.clear(); // 记得清空 res
		cin>>str;
		n=str.size();
		
		str="##"+str+"##"; // 减少特判边界,加上边界符号
		
		bool ok=false; // 特殊判断一开始的局面是否存在 "XX." "X.X" ".XX" 的情况
		rep(i,2,n+1) if(str[i]=='.'){
			bool fl=false;
			if(str[i-1]=='X' && str[i-2]=='X') ok=fl=true;
			if(str[i+1]=='X' && str[i+2]=='X') ok=fl=true;
			if(str[i-1]=='X' && str[i+1]=='X') ok=fl=true;
			if(fl) res.pb(i);
		}
		
		if(ok){
			puts("WINNING");
			rep(i,0,res.size()-1){
				if(!i) cout<<res[i]-1;
				else cout<<' '<<res[i]-1;
			}
			cout<<endl;
			
			continue;
		}
		
		rep(i,2,n+1) if(str[i-2]!='X' && str[i-1]!='X' && str[i]!='X' && str[i+1]!='X' && str[i+2]!='X'){
			tmp=str; 
			tmp[i]='X';
			
			int t=0;
			rep(j,2,n+1){
				if(tmp[j]=='.'){ // 将连续的 "." 区域拿来讨论。
					int l=j;
					while(tmp[j]=='.') j++; 
					j--;
					int r=j;
					
					int len=r-l+1; 
					if(tmp[l-1]=='X') len-=2; 
					if(tmp[r+1]=='X') len-=2;
					len=max(len, 0);

					t^=f[len];
				}
			}
			
			if(!t) res.pb(i);
		}
		
		if(res.size()){
			puts("WINNING");
			rep(i,0,res.size()-1){
				if(!i) cout<<res[i]-1;
				else cout<<' '<<res[i]-1;
			}
			cout<<endl;
		}
		else puts("LOSING\n");
	}	
    return 0;
}	
posted @   HinanawiTenshi  阅读(43)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示