传送门:Treblecross LightOJ - 1229
题意:一排n个位置,有些位置已经放上了字母X,剩下的是空的。两人轮流放置X在空的位置。放置后出现连续3个X的赢。问先手是否有必胜策略?若有,可以放在哪些地方?
思路:肯定是要枚举先手放的位置,然后判断是否能赢,而判断是否能赢我们就要求sg函数了,然而这个题直接求sg函数的话很难求,因为状态太复杂了,巧妙的思路是将连续的空闲位置拿出来,看成一个独立的nim游戏,求在这些空闲位置上放X的sg值,然后求nim和(异或)就是总状态的sg值了。
详细分析:点击打开链接
代码:
#include<bits/stdc++.h>
#define ll long long
#define pi acos(-1)
#define MAXN 210
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
char str[MAXN];
int len, cnt;
int ans[MAXN], sg[MAXN];
bool check()
{
for(int i = 0; i < len - 2; i++)
if(str[i] == 'X' && str[i + 1] == 'X' && str[i + 2] == 'X')
return 1;
return 0;
}
int get_sg(int n)
{
if(sg[n] != -1) return sg[n];
bool vis[MAXN];
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++)
vis[get_sg(max(i - 3, 0)) ^ get_sg(max(n - i - 2, 0))] = 1;
for(int i = 0; i < MAXN; i++)
if(!vis[i])
return sg[n] = i;
}
bool judge()
{
for(int i = 0; i < len; i++)
{
if(str[i] == '.')
{
str[i] = 'X';
if(check())
{
str[i] = '.';
return 0;
}
str[i] = '.';
}
}
int num = 0, ans = 0;
for(int i = 0; i < len; i++)
{
if(str[i] == 'X' || (i > 0 && str[i - 1] == 'X') || (i > 1 && str[i - 2] == 'X') || (i + 1 < len && str[i + 1] == 'X') || (i + 2 < len && str[i + 2] == 'X'))
{
ans ^= get_sg(num);
num = 0;
}
else
num++;
}
ans ^= get_sg(num);
return !ans;
}
void solve()
{
cnt = 0;
for(int i = 0; i < len; i++)
{
if(str[i] == '.')
{
str[i] = 'X';
if(check() || judge())
ans[cnt++] = i + 1;
str[i] = '.';
}
}
}
int main()
{
int T;
cin >> T;
int kase = 1;
while(T--)
{
memset(sg, -1, sizeof(sg));
sg[0] = 0;
scanf(" %s", str);
len = strlen(str);
solve();
printf("Case %d:", kase++);
if(cnt == 0) printf(" 0");
else
for(int i = 0; i < cnt; i++)
printf(" %d", ans[i]);
puts("");
}
return 0;
}