题目链接:https://leetcode-cn.com/problems/nim-game/description/
您和您的朋友,两个人一起玩 Nim游戏:桌子上有一堆石头,每次你们轮流拿掉 1 到 3 块石头。 拿掉最后一块石头的人就是胜利者。由您来开局。
你们两个都是聪明人,相信都有最佳的游戏策略。 请编写一个函数,来判断您是否可以在给定的石头数量的情况下赢得游戏。
比方说,如果堆中有4块石头,那么你永远不会赢得比赛:无论你拿走的是 1块,2块 还是 3块 石头,最后一块石头总是会被你的朋友拿走。
这里做一个抽象,假设一推里面有n个石头,每次可以取 1-m 个石头。
显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。这里我们就有一个想法了,假设这个石头推为 (m+1)的倍数,那么第一个人取k( 1 <= k <= m)个,只要第二个人取 (m+1-k)个石头,那么必定状态能回到最初的状态,m+1个。因为每个人都是很聪明的,取的石头的个数一定要对自己有利。那么,假设最初石头推不为 (m+1)的倍数。n=(m+1)r+s,那么第一个人只要取s个石头必定能获得胜利,反之,如果s == 0 ,那么第一个人必输。
即,若n=k*(m+1),则后取着胜,反之,存在先取者获胜的取法。n%(m+1)==0. 先取者必败。
AC代码:
1 public boolean canWinNim(int n) { 2 3 return (n % 4) == 0 ? false : true; 4 }
(扩展):尼姆博弈
题型
尼姆博弈模型,大致上是这样的:
有3堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取1个,多者不限,最后取光者得胜。
获胜情况对先取者的讨论
异或结果为0,先取者必败,无获胜方法。后取者获胜;
结果不为0,先取者有获胜的取法。
拓展
任给N堆石子,两人轮流从任一堆中任取(每次只能取自一堆),取最后一颗石子的人获胜,问先取的人如何获胜?
根据上面所述,N个数异或即可。如果开始的时候T=0,那么先取者必败,如果开始的时候T>0,那么只要每次取出石子使得T=0,即先取者有获胜的方法。
最后一个奇异局势是(0,0...,0)。另一个奇异局势是(n,n,0...0),只要对手总是和我拿走一样多的物品,最后会面对(0,0...,0)。
SG函数模板:
1 import java.util.Arrays; 2 3 class Main { 4 // N 表示可以取的个数 5 // MAXN表示每堆石头的最大数 6 public static final int N = 20; 7 public static final int MAXN = 1000; 8 public static int[] f = new int[N]; 9 public static int[] SG = new int[MAXN + 10]; 10 public static int[] S = new int[MAXN + 10]; 11 12 public static void main(String[] args) { 13 14 15 } 16 17 void getSg(int n) { 18 int i, j; 19 Arrays.fill(SG, 0); 20 for (i = 1; i <= n; i++) { 21 Arrays.fill(S, 0); 22 for (j = 0; f[j] <= i && j < N; j++) 23 S[SG[i - f[j]]] = 1; 24 for (j = 0;; j++) 25 if (S[j] == 0) { 26 SG[i] = j; 27 break; 28 } 29 } 30 } 31 }
1 /* 2 按照卡中心校园招聘的要求,HR小招和小商需要从三个科室中(分别为A、B、C)抽派面试官去往不同城市。 3 两名HR按照以下规定轮流从任一科室选择面试官:每次至少选择一位,至多选择该科室剩余面试官数。最先选不到面试官的HR需要自己出差。 4 假设HR小招和小商都不想出差且每次选择都采取最优策略,如果是小招先选,写一个函数来判断她是否需要出差。如果不需要出差,请给出第一步的最优策略。 5 */ 6 // 测试用例 2,0,4 -> c,2 1,8,9 -> 1(需要出差) 7 public class Main{ 8 public static void main(String[] args) { 9 Scanner in = new Scanner(System.in); 10 String string = in.next().toString(); 11 String stringArray[] = string.split(","); 12 int num[] = new int[stringArray.length]; 13 for (int i = 0; i < stringArray.length; i++) { 14 num[i] = Integer.parseInt(stringArray[i]); 15 } 16 int a = num[0]; 17 int b = num[1]; 18 int c = num[2]; 19 int bool = a ^ b ^ c; 20 if (bool == 0) 21 System.out.print(1); 22 else { 23 if ((a ^ b) < c) { 24 System.out.print("C," + (c - (a ^ b))); 25 } 26 if ((a ^ c) < b) { 27 System.out.print("B," + (b - (a ^ c))); 28 } 29 if ((b ^ c) < a) { 30 System.out.print("A," + (a -(b ^ c))); 31 } 32 } 33 } 34 }