《编程之美-微软技术面试心得》这本书中的1.11节的转化问题研究:nim拈游戏研究

来源:互联网 发布:淘宝信誉评级一般 编辑:程序博客网 时间:2024/05/16 11:05

《编程之美-微软技术面试心得》这本书中的1.11节有一个问题:


一堆石块,A和B两个人从里面拿,每次只能拿一个或者任意连续的两个,最后拿到的算赢,先拿者是否有必胜策略:


这个问题较为简单:先拿者有必赢策略,只要按照如下规则拿即可,如果有奇数个石块,先拿者只拿中间的一个,如果偶数块,先拿者拿中间的两个,之后的策略就很简单了,和对手对称的拿就行了,对手怎么拿,先拿者也怎么拿,一定会拿到最后一块。


现在将问题转化一下,如果最后拿到算输的话,先拿者是否还有必胜策略呢??


如果共有1个石块,有必赢策略

2:有

3:有

4:无

5:有(拿掉边上的一个,就使得对方没有必赢策略)

6:有(拿掉边上的两个)


规则1:如果n没有必赢策略,那么n+1和n+2一定有必赢策略


给定一堆石块n个,怎样判断先拿者是否有必胜策略呢,就是判断从n个石块任意拿一个或者连续的两个,以后的结果如果没有必赢策略,那么这n个石块的堆就有了必赢策略:

用[x,y]表示一个被拆分了的堆,首先计算[x,y]是否有必赢策略。


规则2:n个石块的一段,拿掉任意一个或者连续的两个以后剩下[x,y],如果任意一种情况没有必赢策略,那么n个一堆就有必赢策略,否则,没有必赢策略


规则3:[x,y]是否有必赢策略可以通过和规则2一样的推理方法,又所有[x,y]的拆分情况是否有必赢策略决定。


规则4:初始条件相关,奇数个1的组,有必赢策略,退化为1个1的组,偶数个1,无必赢策略,退化为两个1的组。


使用以上4个规则,则可以得出一个算法:


package shikuai;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.HashSet;/** *  * 一堆石块,A和B两个人从里面拿,每次只能拿一个或者任意连续的两个,最后拿到的算输,先拿者是否有必胜策略 *  * @author wan *  */public class ShiKuaiProblem {static class ShiKuai implements Comparable<ShiKuai> {int value;ArrayList<ShiKuai> res = new ArrayList<ShiKuai>();public ShiKuai(int v) {this.value = v;}public ArrayList<ShiKuai> splits(int i) {res.clear();if (i == 0) {if (this.value != 1)res.add(new ShiKuai(this.value - 1));} else if (i < this.value - 1) {res.add(new ShiKuai(i));res.add(new ShiKuai(this.value - i - 1));} elseres.add(this);return res;}public ArrayList<ShiKuai> splits2(int i) {res.clear();if (i == 0) {if (this.value > 2)res.add(new ShiKuai(this.value - 2));} else if (i < this.value - 2) {res.add(new ShiKuai(i));res.add(new ShiKuai(this.value - i - 2));} elseres.add(this);return res;}@Overridepublic int compareTo(ShiKuai o) {return this.value - o.value;}@Overridepublic String toString() {return String.valueOf(this.value);}}static class ShiKuaiGroup {HashSet<ShiKuai> group;ArrayList<ShiKuai> res;public ShiKuaiGroup() {group = new HashSet<ShiKuai>();res = new ArrayList<ShiKuai>();}public ShiKuaiGroup(int... vs) {this();for (int i : vs) {group.add(new ShiKuai(i));}}@Overridepublic int hashCode() {int h = 0;int i = 1;for (ShiKuai sk : group) {h += Math.pow(sk.value, i++);}return h;}@Overridepublic boolean equals(Object obj) {ShiKuaiGroup o = (ShiKuaiGroup) obj;// if (group.size() != o.group.size())// return false;// if (this.group.containsAll(o.group))// return true;// return false;return this.toString().equals(o.toString());}@Overrideprotected ShiKuaiGroup clone() {ShiKuaiGroup cl = new ShiKuaiGroup();for (ShiKuai sk : this.group) {cl.group.add(new ShiKuai(sk.value));}return cl;}@Overridepublic String toString() {int[] arr = new int[this.group.size()];int i = 0;for (ShiKuai sk : this.group) {arr[i++] = sk.value;}Arrays.sort(arr);StringBuffer sb = new StringBuffer();sb.append("[" + arr[0]);for (int j = 1; j < arr.length; j++) {sb.append(" " + arr[j]);}sb.append("]");return sb.toString();}}static volatile HashMap<String, Boolean> data = new HashMap<String, Boolean>();static long time = 0;static long time1 = 0;static boolean compute(int x) {if (x == 1)return false;if (x == 2)return true;ShiKuaiGroup s1 = new ShiKuaiGroup(x - 1);if (!containsKey(s1)) {// System.out.println("compute " + (x - 1) + " first");compute(x - 1);}ShiKuaiGroup skg = new ShiKuaiGroup(x);ShiKuaiGroup s2 = new ShiKuaiGroup(x - 2);if ((containsKey(s1) && !get(s1)) || (containsKey(s2) && !get(s2))) {put(skg.clone(), true);return true;}if (!containsKey(skg)) {for (int i = 1; i < (x + 1) / 2; i++) {ShiKuaiGroup skg1 = new ShiKuaiGroup(i, x - i - 1);boolean flag = compute(skg1);if (!flag) {put(skg.clone(), true);return true;}}for (int i = 1; i < x / 2; i++) {ShiKuaiGroup skg1 = new ShiKuaiGroup(i, x - i - 2);boolean flag = compute(skg1);if (!flag) {put(skg.clone(), true);return true;}}put(skg.clone(), false);}return get(skg);}private static boolean compute(ShiKuaiGroup skg) {// System.out.println(skg.toString());if (containsKey(skg)) {return get(skg);}ArrayList<ShiKuai> rms = new ArrayList<ShiKuai>();for (ShiKuai sk : skg.group) {if (sk.value == 1) {rms.add(sk);}}if (rms.size() >= 2) {skg.group.remove(rms.get(0));skg.group.remove(rms.get(1));boolean flag = compute(skg);skg.group.add(rms.get(0));skg.group.add(rms.get(1));return flag;}// ShiKuai s = skg.group.get(0);// if (s.value == 1) {// ShiKuai s1 = skg.group.get(1);// if (s1.value == 1) {// boolean flag = compute(skg);// skg.group.add(s1);// skg.group.add(s);// return flag;// } else {// skg.group.add(s1);// skg.group.add(s);// }// } else {// skg.group.add(s);// }ArrayList<ShiKuai> skss = new ArrayList<ShiKuai>();for (ShiKuai shiKuai : skg.group) {skss.add(shiKuai);}for (ShiKuai sk : skss) {skg.group.remove(sk);for (int i = 0; i < (sk.value + 1) / 2; i++) {ArrayList<ShiKuai> sks = sk.splits(i);// System.out.println(sk.value + "\t" + sks);skg.group.addAll(sks);boolean flag = compute(skg);for (ShiKuai shiKuai : sks) {skg.group.remove(shiKuai);}if (!flag) {skg.group.add(sk);put(skg.clone(), true);return true;}}skg.group.add(sk);}for (ShiKuai sk : skss) {skg.group.remove(sk);for (int i = 0; i < sk.value / 2; i++) {ArrayList<ShiKuai> sks = sk.splits2(i);skg.group.addAll(sks);boolean flag = compute(skg);for (ShiKuai shiKuai : sks) {skg.group.remove(shiKuai);}if (!flag) {skg.group.add(sk);put(skg.clone(), true);return true;}}skg.group.add(sk);}put(skg, false);return false;}static int cnt = 0;private static void put(ShiKuaiGroup skg, boolean flag) {//if (skg.group.size() == 2) {long time2 = System.currentTimeMillis();int x = (int) (time2 - time1);// if (!flag || x > 60 * 1000)//if (!flag) {System.out.println(skg.toString() + "\t" + flag + "\t" + x+ "\t" + (time2 - time));time1 = time2;//}//}cnt++;// if (cnt%10000==0)// System.out.println(cnt);data.put(skg.toString(), flag);}private static boolean get(ShiKuaiGroup s1) {return data.get(s1.toString());}private static boolean containsKey(ShiKuaiGroup s1) {return data.containsKey(s1.toString());}public static void main(String[] args) {time = System.currentTimeMillis();time1 = time;put(new ShiKuaiGroup(1), false);put(new ShiKuaiGroup(2), true);put(new ShiKuaiGroup(3), true);put(new ShiKuaiGroup(4), false);put(new ShiKuaiGroup(5), true);put(new ShiKuaiGroup(1, 1), true);put(new ShiKuaiGroup(1, 2), true);put(new ShiKuaiGroup(2, 2), false);System.out.println(compute(20));System.out.println(System.currentTimeMillis() - time);}}


算法复杂度较难精确计算,粗略估计,小于n!