2017国赛
题目描述
小明经常玩 LOL 游戏上瘾,一次他想挑战 K 大师,不料 K 大师说:
"我们先来玩个空格填字母的游戏,要是你不能赢我,就再别玩 LOL 了"。
K 大师在纸上画了一行 n 个格子,要小明和他交替往其中填入字母。
并且:
轮到某人填的时候,只能在某个空格中填入 L 或 O。
谁先让字母组成了"LOL"的字样,谁获胜。
如果所有格子都填满了,仍无法组成 LOL,则平局。
小明试验了几次都输了,他很惭愧,希望你能用计算机帮他解开这个谜。
输入描述
本题的输入格式为:
第一行,数字 n(n<10),表示下面有 n 个初始局面。
接下来,n 行,每行一个串,表示开始的局面。
比如:"******", 表示有 6 个空格。"L****", 表示左边是一个字母 L,它的右边是 4 个空格。
输出描述
要求输出 n 个数字,表示对每个局面,如果小明先填,当 K 大师总是用最强着法的时候,小明的最好结果。
1 表示能赢;
-1 表示必输;
0 表示可以逼平。
输入输出样例
输入
4 *** L**L L**L***L L*****L
输出
0 -1 1 1
题目大意
给出只有L,O,*三个字符组成的字符串,小明和K大师轮流往*的位置填L或O
谁先填出“LOL”,谁就获胜
问小明先填,最后求出小明的最好结果,1表示小明获胜,-1表示小明失败,0表示双方平局
思路:
小明填时,可以直接判断胜负的情况
字符串里存在“LOL”:小明会输,此时-1就是结果
字符串里存在“L*L”,“LO*”,“*OL”:小明会赢,此时1就是结果
字符串里没有“*”时,且不存在“LOL”:当前局面是平局,此时0就是结果
# 填字母的过程
- 填字母的过程,实质上是一个DFS的过程(DFS:可求解是否存在路径等问题),而本题刚好是判断是否直接分出结果
- 每一次填字母,都可以任选一个*的位置,该位置上可以填上L或O
- 搜索直到能分出胜负或者没有*时平局为止
- 剪枝:这个局面先前已经判断过,可直接返回对应的结果。用一个字典dp{}保存结果,键:状态,值:对局结果
解题关键:DFS+记忆状态+剪枝
- 定义一个字典记录所有状态的胜负结果
- 从字符串开始状态进行DFS
- 重复性剪枝:当前局面在字典中出现过,直接返回对应的结果。
- 搜索结束:当前局面可以直接判断结果(三种可以直接判断的结果:LOL、“L*L”,“LO*”,“*OL”、没有“*”时,且不存在“LOL”)
- 如果不能直接判断结果和不能剪枝,那么遍历当前局面所有的*位置,对填L和O的两种情况进行DFS搜索判断
tips:针对上面是否可以直接判断出结果,我们可以通过判断该子串是否在该字符串中来确定,可以用in方法,例如"LOL" in s,存在的话返回True,否则返回False。也可以使用find方法,例如s.find("LOL"),存在的话返回该子串首字符在字符串的下标,否则返回-1
代码
n = int(input())
def dfs(s, n):
if s in di.keys(): # 之前出现过该局面,直接返回结果
return di[s]
if "LOL" in s: # 出现LOL,输了
di[s] = -1
return -1
elif "L*L" in s or "*OL" in s or "LO*" in s: # 赢的局面
di[s] = 1
return 1
elif "*" not in s and "LOL" not in s: # 没有*且没有LOL,平局
di[s] = 0
return 0
flag = -1 # 初始化为最差结果:输,当出现刚好的结果才会替换
for i in range(n):
ls = list(s) # 需要修改字符串,先转成列表
if ls[i] == "*": # 遇到*才能填
ls[i] = 'L' # 小明填L
last_r1 = dfs("".join(ls), n) # dfs求上一轮的结果。"".join(ls)将对局状态转回字符串
if last_r1 == -1: # 上一轮是k大师的局输了
di[s] = 1 # 相当于小明赢了
return 1
if last_r1 == 0: # 上一轮是k大师的局平了
flag = 0 # 相当于小明平了,这里不return 0,因为可能出现更好的结果:赢
ls[i] = 'O' # 小明填L
last_r2 = dfs("".join(ls), n) # dfs求上一轮的结果。"".join(ls)将对局状态转回字符串
if last_r2 == -1: # 上一轮是k大师的局输了
di[s] = 1 # 相当于小明赢了
return 1
if last_r2 == 0: # 上一轮是k大师的局平了
flag = 0 # 相当于小明至少平了,这里不return 0,因为可能出现更好的结果:赢
di[s] = flag # 最终结果
return di[s] # 最终返回对局结果
for i in range(n):
di = {} # 记录所有的状态
s = input() # 每个局面的起始状态
print(dfs(s, len(s)))