填字母游戏
问题背景
小明沉迷 LOL。K 大师与他先玩一个"在空格中填字母"的小游戏:
两人轮流在一行格子中,把某个空格替换为 L 或 O。谁先在整行字符串中形成子串 "LOL" ,谁就获胜 ;若所有格子填满仍无法形成 "LOL",则平局。
游戏规则
- 初始给定一行由 
L、O和*(表示空格)组成的字符串。 - 两名玩家轮流行动,每回合:
- 选择任意一个 
*,将其替换为L或O。 
 - 选择任意一个 
 - 判定:
- 任何时刻,一旦行内出现连续的 "LOL",当前行动的玩家立刻获胜;
 - 若所有 
*均被替换完仍未出现 "LOL",则平局。 
 
目标
对每个给定的初始局面,假设小明先手 且双方都采用最优策略,输出小明能达到的最好结果:
1:先手必胜;0:先手可保平;-1:先手必败。
输入格式
- 第 1 行:整数 
n(n < 10),表示有n个初始局面。 - 接下来的 
n行:每行一个只包含L、O、*的字符串,表示一个初始局面(例如:"******":6 个空格;"L****":左端一个L,其后 4 个空格)。
 
输出格式
- 输出 
n行,每行一个整数,分别对应每个初始局面的最好结果:1、0或-1(含义见上文"目标")。
 
示例
输入
            
            
              in
              
              
            
          
          4
***
L**L
L**L***L
L*****L
        输出
            
            
              out
              
              
            
          
          0
-1
1
1
        c++代码
            
            
              cpp
              
              
            
          
          #include<bits/stdc++.h>
using namespace std;
int n;
string s;
unordered_map<string, int> mp;
int result() {
    if (mp.find(s) != mp.end()) return mp[s];
	if (s.find("LOL") != -1) {mp[s] = -1; return -1;}
	if (s.find("*") == -1) {mp[s] = 0; return 0;}
	int key = -1;
	for (int i = 0; i < s.size(); i++) {
		if (s[i] != '*') continue;
		s[i] = 'L';
		int sym = result();
		if (sym == -1) {
			s[i] = '*';
			mp[s] = 1;
			return 1;
		}
		else if (sym == 0) key = 0;
		s[i] = 'O';
		sym = result();
		if (sym == -1) {
			s[i] = '*';
			mp[s] = 1;
			return 1;
		}
		else if (sym == 0) key = 0;
		s[i] = '*';
	}
	mp[s] = key;
	return key;
}
int main() {
	cin >> n;
	while (n--) {
		cin >> s;
		cout << result() << endl;
	}
	return 0;
}
        题目解析
博弈论(递归+回溯)
如果敌方必赢,那么我方必输,如果敌方必输那么我方必赢。否则可以平局。
            
            
              c++
              
              
            
          
          if (sym == -1) {
    return 1;
}
        int sym()的返回值表示,当前局面,先下棋的人的输赢。
我们枚举先下棋的人这一步的每一种下法。
            
            
              cpp
              
              
            
          
          for (int i = 0; i < s.size(); i++) {
    if (s[i] != '*') continue;
    s[i] = 'L';
    //...省略代码
    s[i] = '*';.//回溯
    //...省略代码
    s[i] = 'O';
    s[i] = '*';//回溯
}
        然后下一步就轮到敌方下。我们再次调用sym函数获得敌方输赢。
            
            
              cpp
              
              
            
          
          s[i] = 'L';
int sym = result();
if (sym == -1) {
    //...
    return 1;
}
        一直枚举每一种下法,直到已经分出胜负才停止。
如果敌方的每一种返回值都是必赢那么我们必输。
如果敌方只要有一个必输那么我们必赢。
否则可以平局。
记忆化搜索
递归过程中会出现很多重复情况,而每一种情况的返回值都是一样的。
为了不重复走我们已经模拟过的情况,我们用unorder_map把我们走过的每一种情况存储下来。
下次我们遇到重复的情况的时候,直接返回存储的值,而不是再次递归搜索。
            
            
              cpp
              
              
            
          
          if (mp.find(s) != mp.end()) return mp[s];