题目大意:有两个人轮流在一个 1 * n的序列上选择一个空的格子放X,若某个人放下X时出现三个连续X,这该人获胜。输入的序列中可能已经包含一些X,保证不包含连续的3个X。
你的任务是判断先手必胜还是先手必败,若先手必胜,输出所有必胜策略的第一步放X的位置。
分析:
若序列中出现形如 XX,或X.X这样的连续的序列,则先手必胜,且必须放在XX的两边或X.X的中间。
若序列中没有这种情况:先看一段连续的空白的序列,当放下一个X时,双方都采取最优策略,则X的旁边已经X的旁边的旁边不会去放X,因为如果放了X,则下一步对方一定能凑出连续的三个X(自己画一段看看)。X以及它半径的范围构成一个禁区不能放X,这样一段连续的序列会被分割成两段,两段分别是两个独立的游戏,因此可以用SG函数。令连续区间的长度为状态,边界状态sg函数值为:sg[0] = 0,sg[1] = sg[2] = sg[3] = 1。其他状态可以枚举 ‘X’ 所在的分割点递推求得:
sg[x] = mes{sg[x - 3],sg[x -4],sg[x - 5],sg[1] ^ sg[x - 6],sg[2] ^ sg[x - 7] …}。
对于求必胜策略:若有XX 或X.X这样的序列,最优解就是那是放在哪些使得出现连续的3个X的位置。对于没有XX和X.X这样的序列,题目中已包括的X会将原序列分割成多段连续的区间。枚举 所有的决策位置点,求一下该位置放X后的sg函数值,必胜决策的位置就是那些放了X后sg函数值为0的位置,注意X不能放在禁区(最优决策不会这样做,这样放直接走向了必胜态,先手输,而且SG函数求的是不在禁区放X的SG值)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e2 + 10;
int t;
char str[maxn];
int sg[maxn];
int vis[maxn * 100];
vector<int> ans;
int getsg() {
int v = 0,last = 0;
int i;
for(i = 1; str[i]; i++) {
if(str[i] == 'X') {
if(!last) {
v ^= sg[i - 3];
}
else {
int p = i - last - 5;
if(p >= 0)
v ^= sg[p];
}
last = i;
}
}
if(last) {
int p = i - last - 3;
if(p > 0)
v ^= sg[p];
}
else {
v ^= sg[i - 1];
}
return v;
}
void solve() {
int i;
for(i = 1; str[i + 1]; i++) {
if(str[i] == 'X' && str[i + 1] == 'X') {
if(i > 1) ans.push_back(i - 1);
if(str[i + 2]) ans.push_back(i + 2);
}
if(str[i] == 'X' && str[i + 2] == 'X') {
ans.push_back(i + 1);
}
}
}
int main() {
scanf("%d",&t);
sg[0] = 0;
sg[1] = sg[2] = sg[3] = 1;
for(int i = 4; i <= 300; i++) {
memset(vis,0,sizeof(vis));
for(int j = 1; j <= i; j++) {
if(j - 2 > 0)
vis[sg[j - 3] ^ sg[i - j - 2 > 0 ? i - j - 2 : 0]] = 1;
else
vis[sg[i - j - 2]] = 1;
}
for(int j = 0; ; j++) {
if(!vis[j]) {
sg[i] = j;
break;
}
}
}
while(t--) {
memset(str,0,sizeof(str));
scanf("%s",str + 1);
ans.clear();
solve();
if(!ans.size()) {
for(int i = 1; str[i]; i++) {
if(str[i] != 'X') {
str[i] = 'X';
if(str[i - 1] != 'X' && str[i - 2] != 'X' && str[i + 1] != 'X' && str[i + 2] != 'X') {
if(getsg() == 0) ans.push_back(i);
}
str[i] = '.';
}
}
}
if(!ans.size()) {
puts("LOSING\n");
}
else {
puts("WINNING");
for(int i = 0; i < ans.size(); i++) {
if(i) printf(" ");
printf("%d",ans[i]);
}
cout << endl;
}
}
return 0;
}