蓝桥杯 试题 历届试题 填字母游戏 博弈+dfs剪枝

问题描述
  小明经常玩 LOL 游戏上瘾,一次他想挑战K大师,不料K大师说:
  “我们先来玩个空格填字母的游戏,要是你不能赢我,就再别玩LOL了”。


  K大师在纸上画了一行n个格子,要小明和他交替往其中填入字母。


  并且:


  1. 轮到某人填的时候,只能在某个空格中填入L或O
  2. 谁先让字母组成了“LOL”的字样,谁获胜。
  3. 如果所有格子都填满了,仍无法组成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

//解题思路:这一题是含有平局的无偏博弈问题。博弈问题一般思路:
f( 当前局势  )
{
    临界条件
    
    t = 负
    for( 所有步数 ){
        t = f(尝试走一步)
        if( t==负 ) return 胜
        if( t==平 ) t = 平
    }
    return t
}
即尝试一步,改变当前局势,交给对方处理。每一方都尽可能希望胜利,其次平局。
这里直接用dfs()会超时,可以用C++ map 将一个局势 和 最终结果一一对应。 map.find()如果未找到 则 == map.end()  string.find()未找到返回-1
 
//实现代码:
#include<iostream>
#include<string>
#include<map>
using namespace std;

//输入 
int n;
string str;//当前状态 

map<string,int> m;//键值对 用来把相同状况剪枝

int dfs(string str)// 返回  1 0 -1
{
    if( m.find(str)!=m.end() ){
        return m[str];//如果重复 剪枝 
    }
    
    if( (str.find("LO*")+1)||(str.find("*OL")+1)||(str.find("L*L")+1)){
        return m[str] = 1;//如果发现任意一个 则胜利(+1后返回值>=1 未找到返回0) 
    }
/***    上面代码如果改为 
    if( str.find("LOL") ){
        return m[str] = -1;//如果发现任意一个 则胜利(+1后返回值>=1 未找到返回0) 
    } 
        逻辑上也是对的 但运行会超时 大概是多了不必要的递归  ***/
    if( str.find('*')==-1 ){
        return m[str] = 0;//没有空位* 则平局 
    }
    
    int flat = -1;
    for(int i=0; i<str.length(); i++)
    {
        if( str[i] != '*' )
        {
            continue;
        }
        //尝试两种方式 
        str[i] = 'L';
        if( dfs(str)==-1 ){
            str[i] = '*';//这里要先回溯为传入时的状态 再存入map 
            return m[str] = 1;
        }
        
        if( dfs(str)==0 ){
            flat = 0;
        }
        
        str[i] = 'O';
        if( dfs(str)==-1 ){
            str[i] = '*';
            return m[str] = 1;
        }
        
        if( dfs(str)==0 ){
            flat = 0;
        }
        
        str[i] = '*';//回溯 
    }
    
    return m[str] = flat;
}

void solve()
{
    int res = dfs(str);
    cout<<res<<endl;
}

int main()
{
    cin>>n;
    while( n-- )
    {
        cin>>str;
        solve();
    }
    return 0;
}

 先回溯再存如map:回溯前的状态是返回-1的,即是必输的状态。

posted @ 2020-05-19 23:00  代码改变头发  阅读(471)  评论(0编辑  收藏  举报