【座右铭】1. 想要成为行家,就必须尝试解决大量的问题;
2. 解决大量问题并不代表能解决所有问题,而是表示解决下一个问题的几率变大了
1.有65颗宝石,两个人来取,规则是每次必须且只能取1-3颗,取完后,手中为偶数颗的取胜,请编制一个先手必胜的取法程序【问题来源于论坛】
第一部分:思路
1.定义一个N*4的矩阵的矩阵:
| 1个石头 | 2个石头 | 3个石头 | 4个石头 | … |
[奇,奇] | (1,1) | (0,1) | (1,3) | (0,1) |
|
[奇,偶] | (0,1) | (1,1) | (0,1) | (1,3) |
|
[偶,奇] | (0,1) | (1,2) | (0,2) | (-1,1) |
|
[偶,偶] | (-1,1) | (0,2) | (1,2) | (0,2) |
|
第2列的二元组分别表示第一个人是否获胜,以及如何取石头。例如(1,1)表示第一个人取1个石头肯定获胜;(0,1)表示最好的结局是平均,取1颗石头;(-1,1)表示第一个人肯定会输,取1颗石头
2.在初始化1、2、3个石头后,以后的胜负可依次推导出,例如:
设T(i,[奇,奇])表示二人在第i个石头(i>3)前,手上的石头数都为奇数,则有:
T(i,[奇,奇]) = max{-1*T(i-1,[奇,偶]),-1*T(i-2,[奇,奇]),-1*T(i-3,[奇,偶])},其中“-1*T(i-1,[奇,偶])”表示第一个人取了1个石头后,手上就有偶数个石头。轮到第二个人取时,就把第一个人和第二个人调换下,然后从矩阵的第i-1列获知胜负,然后将胜负再调换。
第二部分:Java代码,不考虑异常情况
public class Game {
private Game(){}
/**
* 对外接口,返回N*4*2的矩阵
* @param number
* 输入的石头数
* @return
* 返回N*4*2的矩阵
*/
public static int[][][] start(int number)
{
number = number>3?number:3;
int[][][] path = new int[number+1][4][2];
//初始化1颗石头
path[1][0][0] = 1; path[1][0][1] = 1;
path[1][1][0] = 0; path[1][1][1] = 1;
path[1][2][0] = 0; path[1][2][1] = 1;
path[1][3][0] = -1; path[1][3][1] = 1;
//初始化2颗石头
path[2][0][0] = 0; path[2][0][1] = 1;
path[2][1][0] = 1; path[2][1][1] = 1;
path[2][2][0] = 1; path[2][2][1] = 2;
path[2][3][0] = 0; path[2][3][1] = 2;
//初始化3颗石头
path[3][0][0] = 1; path[3][0][1] = 3;
path[3][1][0] = 0; path[3][1][1] = 1;
path[3][2][0] = 0; path[3][2][1] = 2;
path[3][3][0] = 1; path[3][3][1] = 2;
for(int i=4;i<=number;i++)
{
for(int j=0;j<4;j++)
{
//拿1颗石头
int p = exchange(j,1);
path[i][j][0] = -1*path[i-1][p][0];
path[i][j][1] = 1;
//拿2颗石头
p = exchange(j,2);
int result = -1*path[i-2][p][0];
if(result>path[i][j][0])
{
path[i][j][0] = result;
path[i][j][1] = 2;
}
//拿3颗石头
p = exchange(j,3);
result = -1*path[i-3][p][0];
if(result>path[i][j][0])
{
path[i][j][0] = result;
path[i][j][1] = 3;
}
}
}
return path;
}
/*
* 奇偶对调
*/
static int exchange(int index, int stone)
{
//第一人的手上石头数发生改变
if(stone==1||stone==3)
{
switch(index)
{
case 0:index=2;break;
case 1:index=3;break;
case 2:index=0;break;
default: index=1;
}
}
switch(index)
{
case 0:return 0;
case 1:return 2;
case 2:return 1;
default: return 3;
}
}
}
第三部分:测试用例
在输入石头数为65时,结果如下所示:
[偶,偶]:-1 0 1 0 1 0 1 0 -1 0 1 0 1 0 1 0 -1 0 1 0 1 0 1 0 -1 0 1 0 1 0 1 0 -1 0 1 0 1 0 1 0 -1 0 1 0 1 0 1 0 -1 0 1 0 1 0 1 0 -1 0 1 0 1 0 1 0 -1
从该序列中可知,当石头数为65时,先出手必败。而且还可从发现如下规律:
1)当石头数模8余1时,先手必败
2)当石头数模8余3,5,7时,先手必胜
3)当石头数模8余0,2,4,6时,平局