LightOJ 1229 Treblecross(SG函数打表 + 遍历)题解
题意:给你一串含“.”和“X”的字串,每次一个玩家可以把‘."变成“X”,谁先弄到三个XXX就赢。假如先手必赢,输出所有能必赢的第一步,否则输出0。
思路:显然如果一个X周围两格有X那么肯定能一步变成XXX,所以两个人都要避免在自己回合产生这种情况。如果一开始就存在上述情况,那么肯定是那一步。否则我遍历每一个空格看看能不能下这一步。满足我在这个空格变成“X”不会造成上述情况,然后算出nim和是否留给对手一个必败态。
设sg[x]表示长度为x的空格的sg函数,然后我遍历1~x位置变成“X”,那么空格键会被我分成两块(比如.....我在3位置下X,那么空格被我分成了左0右0两块,注意X旁边两块不能动)。
代码:
#include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> typedef long long ll; const int maxn = 200 + 10; const int seed = 131; const ll MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; int sg[maxn], s[maxn], ans[maxn], pos; char str[maxn]; void getSG(){ sg[0] = 0; for(int i = 1; i < maxn; i++){ memset(s, 0, sizeof(s)); for(int j = 1; j <= i; j++){ int t = 0; if(i - j - 2 >= 0) t ^= sg[i - j - 2]; if(j - 3 >= 0) t ^= sg[j - 3]; s[t] = 1; } for(int j = 0; j < maxn; j++){ if(!s[j]){ sg[i] = j; break; } } } } bool check(){ ll ret = 0; int num = 0, len = strlen(str); for(int i = 0; i < len; i++){ if(str[i] == 'X'){ if((i >= 1 && str[i - 1] == 'X') || (i >= 2 && str[i - 2] == 'X') || (i + 1 < len && str[i + 1] == 'X') || (i + 2 < len && str[i + 2] == 'X')){ return false; } if(str[i - num - 1] == 'X' && i - num - 1 >= 0) num -= 2; num -= 2; if(num >= 0) ret ^= sg[num]; num = 0; } else{ num++; } } if(str[len - 1 - num] == 'X' && len - 1 - num >= 0) num -= 2; if(num >= 0) ret ^= sg[num]; return ret == 0; } void solve(){ pos = 0; int len = strlen(str); for(int i = 0; i < len; i++){ if(str[i] == '.'){ str[i] = 'X'; if(i + 1 < len && i - 1 >= 0 && str[i + 1] == 'X' && str[i - 1] == 'X'){ ans[pos++] = i + 1; } else if(i + 1 < len && i + 2 < len && str[i + 1] == 'X' && str[i + 2] == 'X'){ ans[pos++] = i + 1; } else if(i - 2 >= 0 && i - 1 >= 0 && str[i - 2] == 'X' && str[i - 1] == 'X'){ ans[pos++] = i + 1; } else if(check()){ ans[pos++] = i + 1; } str[i] = '.'; } } } int main(){ int T, Case = 1; getSG(); scanf("%d", &T); while(T--){ scanf("%s", str); solve(); printf("Case %d:", Case++); if(pos == 0) printf(" 0\n"); else{ for(int i = 0; i < pos; i++){ printf(" %d", ans[i]); } printf("\n"); } } return 0; }