捡石子游戏
有m个有标记的格子,每个格子里面有或没有石子,每个格子最多放n-1个石子。两个人轮流拿石子,只能从单个格子中拿走任意数量的石子。拿到最后一个石子的人失败。
有标记意味着【0,1】!=【1,0】
每个格子最多放n-1个,所以每个格子有n种状态。
枚举这些状态组合的所有情况,则需要
这么多个空间
状态顺序为 23543 排列的灯,应该位于数列下标为
位置(个数则要+1)
要想知道数组下标表示的状态顺序为多少,则只需用除n取余法即可得出。
若此时n-1为3,m为5.则n为4 要知道下标27表示的状态顺序是多少,则用除4取余法,得到余数分别为 3 2 1 补齐m个数。
则该下标的状态顺序为【0,0,1,2,3】
当轮到自己拿时,怎么判断当前状态顺序是必胜态还是必败态呢?
1.我们知道当状态顺序全为0时,最后一个肯定是对方拿走了,所以此时是必胜态。 2.那么如果我本回合拿完,必定会使对方变为必胜态,则认定我本回合状态为必败态。 3.同理如果我本回合拿完,有一种可能使对方变为必败态,则认定我本回合状态为必胜态。
有了上面三点,就可以开始用下标慢慢遍历了。步骤为
1.将下标转化为状态顺序(即数组) 2.从数组找一个不为0的元素开始 3.将其-1,判断是否满足必败态,不满足则再-1继续判定,直到减为0 4.将数组恢复成未改变的值。找一个新的不为0的元素重复第3步骤。 5.循环往复,一直到找不到新的不为0的数字为止。 6.在以上过程中,只要有一次判断满足必败态,则当前状态顺序(下标)为必胜态。结束循环。 7.若是循环结束也没有判断满足必败态,则当前状态顺序(下标)为必败态。
补充,实际上有标记不影响游戏结果,所以判断一个状态顺序(即数组)的胜败态之前可以先看该数组下标从0->n是否为从大到小排列
若不是,则调换顺序后,将状态顺序(即数组)转换回下标,对应下标的胜败态即为当前下标的胜败态。
结果输出时也可以采用此方法,即只显示数组下标从0->n为从大到小排列的状态顺序(即数组)。
附参考代码与运行结果。
参考代码
package com.demo;
import java.util.Arrays;
public class Demo {
public static int n;
public static int m;
public static int[] radix;
public static int[] nums;
//判断赢态还是输态
//规则:只能拿走数组内一个元素的部分或全部值,拿完后要是有一种可能让数组变为输态,则当前态为赢态。否则当前态为输态。
public static int v(int index){
int [] temp = tenToN(index);
int [] a = new int[m];
//if nums -n = f
for (int i = 0; i < m; i++) {
System.arraycopy(temp,0,a,0,m);
while(a[i]>0){
a[i]=a[i]-1;
if(nums[nToTen(a)]==-1){
return 1;
}
}
}
return -1;
}
public static void main(String[] args) {
//初始化条件
n=4;
m=6;
radix = new int[m];
for (int i = 0; i < m; i++) {
radix[i] = (int)Math.pow(n,i);
}
int num = (int)Math.pow(n,m);
nums = new int[num];
//设置第一个状态
nums[0]=1;
for (int i = 1; i < num; i++) {
nums[i] = v(i);
}
int temp[];
for (int i = 0; i < num; i++) {
temp = tenToN(i);
if(judgmentOrder(temp)){
System.out.println(Arrays.toString(reverse(temp))+":"+nums[i]);
}
}
}
//判断数组是否满足从大到小排列
public static boolean judgmentOrder(int temp[]){
for (int i = 0; i < temp.length-1; i++) {
if(temp[i]<temp[i+1]){
return false;
}
}
return true;
}
//把一个数组逆序输出
public static int[] reverse(int []temp){
int [] temp1 = new int[temp.length];
for (int i = 0; i < temp.length; i++) {
temp1[i] = temp[temp.length-1-i];
}
return temp1;
}
//十进制转n进制
public static int[] tenToN(int index){
int [] temp = new int[m];
for (int i = 0; i < m; i++) {
temp[i] = index%n;
if (index/n>0){
index/=n;
}else{
break;
}
}
return temp;
}
//n进制转10进制
public static int nToTen(int[] temp){
int index = 0;
for (int i = 0; i < m; i++) {
index = index+temp[i]*radix[i];
}
return index;
}
}
运行结果
[0, 0, 0]:1 [0, 0, 1]:-1 [0, 0, 2]:1 [0, 0, 3]:1 [0, 0, 4]:1 [0, 0, 5]:1 [0, 1, 1]:1 [0, 1, 2]:1 [0, 1, 3]:1 [0, 1, 4]:1 [0, 1, 5]:1 [0, 2, 2]:-1 [0, 2, 3]:1 [0, 2, 4]:1 [0, 2, 5]:1 [0, 3, 3]:-1 [0, 3, 4]:1 [0, 3, 5]:1 [0, 4, 4]:-1 [0, 4, 5]:1 [0, 5, 5]:-1 [1, 1, 1]:-1 [1, 1, 2]:1 [1, 1, 3]:1 [1, 1, 4]:1 [1, 1, 5]:1 [1, 2, 2]:1 [1, 2, 3]:-1 [1, 2, 4]:1 [1, 2, 5]:1 [1, 3, 3]:1 [1, 3, 4]:1 [1, 3, 5]:1 [1, 4, 4]:1 [1, 4, 5]:-1 [1, 5, 5]:1 [2, 2, 2]:1 [2, 2, 3]:1 [2, 2, 4]:1 [2, 2, 5]:1 [2, 3, 3]:1 [2, 3, 4]:1 [2, 3, 5]:1 [2, 4, 4]:1 [2, 4, 5]:1 [2, 5, 5]:1 [3, 3, 3]:1 [3, 3, 4]:1 [3, 3, 5]:1 [3, 4, 4]:1 [3, 4, 5]:1 [3, 5, 5]:1 [4, 4, 4]:1 [4, 4, 5]:1 [4, 5, 5]:1 [5, 5, 5]:1