《编程之美-微软技术面试心得》这本书中的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));
} else
res.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));
} else
res.add(this);
return res;
}
@Override
public int compareTo(ShiKuai o) {
return this.value - o.value;
}
@Override
public 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));
}
}
@Override
public int hashCode() {
int h = 0;
int i = 1;
for (ShiKuai sk : group) {
h += Math.pow(sk.value, i++);
}
return h;
}
@Override
public 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());
}
@Override
protected ShiKuaiGroup clone() {
ShiKuaiGroup cl = new ShiKuaiGroup();
for (ShiKuai sk : this.group) {
cl.group.add(new ShiKuai(sk.value));
}
return cl;
}
@Override
public 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!