填字母游戏(博弈问题)

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


解这道题用到博弈问题的递归思路。

小明填一个字母后,让大师去填字母,大师如果输了,代表小明填这个位置能赢。

判断所有小明可以填的位置 *    只要 有一个位置 能让小明赢,小明就赢了


如果判断到了最后,小明都赢不了,需要判断这些位置中出现过平局没有。

有平局,小明 平局,没有平局小明只能输


这道题难点:不同于其他博弈问题,存在平局。

考虑平局问题,最好的结果,小明赢,否则判断能不能平局,不能平局,只能输。


import java.util.Scanner;

public class Main {

    public static void main(String[] args) { 
    	Scanner input = new Scanner(System.in);
    	int n = input.nextInt();
    	while(--n>=0){
    		String in = input.next();
    		System.out.println(f(in.toCharArray()));
    	}
    	
    	
    }  
    
    public static int f(char[] arr){
    	String s = new String(arr);
    	//如果LOL已经出现了那么说明已经输了
    	if(s.contains("LOL"))return -1;
    	
    	//如果不存在可以填写的位置说明平局
    	if(!s.contains("*"))return 0;
    	
    	//用来记录平局出现过没有?
    	boolean ping = false;
    	
    	
    	//模拟所有可以填字母的位置
    	for(int i=0;i<arr.length;i++){
    		//不可以填字母跳过
    		if(arr[i]!='*')continue;
    		
    		
    		//这里之所以使用try finally 因为,finally可以在return 之前执行,方便了回溯
    		try {
    			//模拟填入L
    			arr[i] = 'L';
				switch (f(arr)) {
				//如果返回值为-1 说明 我填写字母后,下一个人 输了,他输了我肯定赢
				case -1:
					return 1;
				//出现平局
				case 0:
					ping = true;
					break;
				}
				
				//模拟填入O
				arr[i] = 'O';
				switch (f(arr)) {
				case -1:
					return 1;
				case 0: 
					ping = true;
					break;
				}
			} finally {
				//回溯,这个位置还变成*
				arr[i] = '*';
			}
    	}
    	
    	//存在平局。
    	return ping?0:-1;
    }
    
    
    
}  

刚刚的问题存在 重复数据递归 。使用动态规划减少运算量。

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) { 
    	Scanner input = new Scanner(System.in);
    	int n = input.nextInt();
    	while(--n>=0){
    		String in = input.next();
    		System.out.println(f(in.toCharArray()));
    	}
    	
    	
    }  
    
    //缓存
    static Map<String,Integer> cache = new HashMap<String,Integer>();
    
    public static int f(char[] arr){
    	String s = new String(arr);
    	
    	//判断该过程是否出现
    	if(cache.containsKey(s)){
    		return cache.get(s);
    	}
    	
    	//如果LOL已经出现了那么说明已经输了
    	if(s.contains("LOL")){
    		cache.put(s, -1);
    		return -1;
    	}
    	
    	//如果不存在可以填写的位置说明平局
    	if(!s.contains("*")){
    		cache.put(s, 0);
    		return 0;
    	}
    	
    	//用来记录平局出现过没有?
    	boolean ping = false;
    	
    	
    	//模拟所有可以填字母的位置
    	for(int i=0;i<arr.length;i++){
    		//不可以填字母跳过
    		if(arr[i]!='*')continue;
    		
    		
    		//这里之所以使用try finally 因为,finally可以在return 之前执行,方便了回溯
    		try {
    			//模拟填入L
    			arr[i] = 'L';
    			
				switch (f(arr)) {
				//如果返回值为-1 说明 我填写字母后,下一个人 输了,他输了我肯定赢
				case -1:
					//存放该过程
					cache.put(s,1);
					return 1;
				//出现平局
				case 0:
					ping = true;
					break;
				}
				
				//模拟填入O
				arr[i] = 'O';
				switch (f(arr)) {
				case -1:
					//存放该过程
					cache.put(s,1);
					return 1;
				case 0: 
					ping = true;
					break;
				}
			} finally {
				//回溯,这个位置还变成*
				arr[i] = '*';
			}
    	}
    	
    	//存在平局。
    	return ping?0:-1;
    }
    
    
    
}  

学习简单的博弈问题:

点击我

动态规划:

点击我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SUNbrightness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值