Java B组蓝桥杯第八届国赛:填字母游戏

38 篇文章 1 订阅
32 篇文章 2 订阅

标题:填字母游戏

小明经常玩 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

资源约定:
峰值内存消耗 < 256M
CPU消耗  < 1000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

这个下棋类的题,之前没碰见过,因此不会做,容易想的复杂。大师用最强着法,百度一搜才知道,“着法”:指下棋路数,或者武术动作。这句话看起来很牛掰。

我一开始理解的是,轮到小明下棋用全排列。轮到大师就用一些能想到的小套路去对应下棋,比如棋盘存在L***L,轮到大师下棋,中间是不会下L,也不会在两边下O。如果没有合适的小套路,在用全排列。可是这不太可能,因为这各种各样的棋盘状态显然不可能短时间实现。最后做出来之后发现其实一点用没有,就是用来误导人的。

后来没思路,在百度找到一个大佬的C++代码,测试通过,然后进行学习,发现思路:

(1)这道题还是普普通通的全排列。(多气人)

(2)大师和小明用的一模一样的下棋方式。

测试网址:https://www.dotcpp.com/oj/problem1845.html

贴上代码:注释应该很详细,不懂就问

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

public class Main {
	String s = "";//存字符串
	Map<String, Integer> map = new HashMap<String, Integer>();//判重,去除棋盘重复的结果
	public Main() {
		Scanner sn = new Scanner(System.in);
		int q = sn.nextInt();
		sn.nextLine();
		for (int i = 0; i < q; i++) {
			s = sn.nextLine().trim();
			System.out.println(check());
			map.clear();
		}

	}

	public int check() {
		// 检验是否含有可以使一方胜利的棋局
		if (s.indexOf("LO*") != -1)
			return 1;
		if (s.indexOf("L*L") != -1)
			return 1;
		if (s.indexOf("*OL") != -1)
			return 1;
		// 无子平局
		if (s.indexOf('*') == -1)
			return 0;
		// 当前棋盘的状态已经存在,只要知道当前状态有结果,不需要管理谁赢谁输,只要返回结果
		if (map.get(s) != null)
			return map.get(s);
		int t = -1;// 记录结果
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '*')// 可以下棋
			{
				//这里我为什么转成字符数组,当然可以直接用字符串拼接
                //但似乎需要if语句判断吧,我拼接有时候报错,因此换成数组就不会了
				char[] c = s.toCharArray();
				c[i] = 'L';// 下L
				s = String.valueOf(c);
				/*
				 * 这里的a1要加符号 我们只单单看第一层,不看递归里面,看深了不好理解 
                 * 我们的每一次check()只是判断当前状态有没有赢的路径,没有规定谁在下棋
				 * 我们来看第一次调用check(),也就是第一层
                 * t表示小明赢得可能,初始值为-1,必输 第一层内调用的check()
                 * 表示进入到大师下棋后的输赢状态
				 * 那么-check()就会表示小明的结果,因为大师赢,小明就输
                 * 或者大师输小明就赢,或者平局为0,负号无影响
				 * 因此t=max(t,-check)为小明能否赢的最好情况。 
                 * 然后返回t输出。 理解了上面说的之后,进入递归,就容易理解了,大师下棋和上述类似
				 */
				t = max(t, -check());
				if (t == 1)
					return 1; // 如果当前状态已经赢了,直接返回1,不需要往下进行了
				c[i] = 'O';// 下O
				s = String.valueOf(c);
				t = max(t, -check());
				c[i] = '*';
				s = String.valueOf(c);// 还原
				if (t == 1)
					return 1; // 如果当前状态已经赢了,直接返回1,不需要往下进行了
			}
		}
		map.put(s, t);// 存放当前棋局,以及对应的输赢状态,用于判重
		return t;
	}

	public int max(int a, int b) {
		return a > b ? a : b;
	}

	public static void main(String[] args) {
		new Main();

	}
}

测试100分通过。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值