蓝桥杯题解-取球博弈1

题目

今盒子里有 n个小球,A 、B两人轮流从盒中取球,每个人都可以看到另一个人取了多少个,
也可以看到盒中还剩下多少个,并且两人都很聪明,不会做出错误的判断。
我们约定:
每个人从盒子中取出的球的数目必须是: 1,3 ,7或者 8个。
轮到某一方取球时不能弃权!
A先取球,然后双方交替取球,直到取完。
被迫拿到最后一个球的一方为负方(输方)
请编程确定出在双方都不判断失误的情况下,对于特定的初始球数, A是否能赢?
程序运行时,从标准输入获得数据,其格式如下:
先是一个整数 n(n<100),表示接下来有n个整数。然后是 n个整数,每个占一行(整数 <10000),表示初始球数。
程序则输出 n行,表示A 的输赢情况(输为 0,赢为1 )。
例如,用户输入:
4
1
2
10
18
则程序应该输出:
0
1
1
0

递归解法

递归解法比较容易想到:

f(int n)
  if(n==0) 对方把球取完了,我赢了,return true;
  if(n>=1 && 我取1个,对方一定为负) return true;
  if(n>=3 && 我取3个,对方一定为负) return true;
  if(n>=7 && 我取7个,对方一定为负) return true;
  if(n>=8 && 我取8个,对方一定为负) return true;
  return false;

代码:

  public static boolean pick1(int n){
    if (n==0)
      return true;
    if (n>=1&&!pick1(n-1))
      return true;
    if (n>=3&&!pick1(n-3))
      return true;
    if (n>=7&&!pick1(n-7))
      return true;
    if (n>=8&&!pick1(n-8))
      return true;
    return false;
  }

处理输入输出的main方法:

  public static void main(String[] args) {
    Scanner scan = new Scanner(System.in);
    int n = scan.nextInt();
    int[] x = new int[n];
    for (int i = 0; i < x.length; i++) {
      x[i] = scan.nextInt();
    }
    scan.close();
    for (int i = 0; i < x.length; i++) {
      System.out.println(pick1(x[i]) ? 1 : 0);
    }
  }

但是这种解法有个问题,就是n很大的时候,效率太低,因为重复计算项很多。

记忆型递归

一般解决这类重复计算的问题,可以用记忆性递归。

  static Map<Integer, Boolean> cache = new HashMap<>();

  /**
   * @param n  代表当前所剩球的个数
   * @return true:我方赢
   */

  static boolean pick2(int n) {
    //对方拿完了,我就赢了
    if (n == 0)
      return true;
    if (cache.get(n) != null)
      return cache.get(n);
    //大于1,我取1,剩下局面(n-1)对方输,则我方赢
    if (n >= 1 && !pick2(n - 1)) {
      cache.put(n, true);
      return true;
    }
    if (n >= 3 && !pick2(n - 3)) {
      cache.put(n, true);
      return true;
    }
    if (n >= 7 && !pick2(n - 7)) {
      cache.put(n, true);
      return true;
    }
    if (n >= 8 && !pick2(n - 8)) {
      cache.put(n, true);
      return true;
    }
    cache.put(n, false);
    return false;
  }

动态规划

也可以先写出递推式,从小数字递推到N:

f(N) = true | f(N-1)=false或者f(N-3)=false或者f(N-7)=false或者f(N-8)=false
f(N) = false | N=1

上代码:

  public static boolean pick(int n) {
    if (n <= 0)
      return false;
    boolean[] state = new boolean[n + 1];
    state[1] = false;

    for (int i = 2; i <= n; i++) {
      boolean flag3;
      if (i - 3 <= 0)
        flag3 = false;
      else
        flag3 = !state[i - 3];

      boolean flag7;
      if (i - 7 <= 0)
        flag7 = false;
      else
        flag7 = !state[i - 7];

      boolean flag8;
      if (i - 8 <= 0)
        flag8 = false;
      else
        flag8 = !state[i - 8];
      //这就是递推式对应的代码
      state[i] = !state[i - 1] || flag3 || flag7 || flag8;
    }
    return state[n];
  }

关于从递归到递推的一般过程请看动态规划(一)DynamicProgramming - CSDN博客

这里还有蓝桥杯大赛的辅导视频

发布了127 篇原创文章 · 获赞 97 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/zhengwei223/article/details/78608596