您的位置:首页 > 编程语言

《编程之美》读书随笔之二:中国象棋将帅问题

2013-08-23 16:11 239 查看
最近工作之余在翻编程之道这本书,不久前听说这本书不错,就赶紧入手,翻着前面赶紧还不错,刚买不久,目前就看了中国象棋将帅问题这一节,废话不多说了,看问题,书上的原话是这样的:下过象棋的朋友都知道,双方的“将”和“帅”相隔很远,并且不能照面,在象棋的残局中,许多高手能利用这一规则走出精妙的杀招。将(A),帅(B)两子只能在自己的3*3的方格内运动,不能走对角线,只能横着或者竖直着走,请写出一个程序,输出A,B的所有合法的位置。要求代码中只有一个变量。

看到这个题目之后,思考了一下,每个子只能在九个位置上,分别设置成为1、2、...、9,那么就变成了A、B的值不能同时满足某个条件。按照书上的说法,这个程序的大体框架就是先遍历A的位置,然后遍历B的位置,判断A,B是否满足条件,如果满足就输出A,B的值。做过一些简单算法的都能看出来,这个题目本身不难,难就难在只能用一个变量,一个变量要存储两个信息,需要思考一下。书上列出了三种解法。

第一种:

#define HALF_BITS_LENGTH  4    //存储单元长度的一半,即4位
#define FULLMASK 255    //存储单元全部bit的mask(掩码),在二进制表示中,是11111111
#define LMASK (FULLMASK << HALF_BITS_LENGTH)   //左四位,11110000
#define RMASK (FULLMASK >> HALF_BITS_LENGTH)   //右四位。00001111
#define RSET(b,n) (b = ((LMASK &b) | (n)))  //将b的右四位设置为n
#define LSET(b,n) (b = ((RMASK &b) | (n) << HALF_BITS_LENGTH)) //将b左四位设置为n
#define RGET(b) (RMASK&b)  //得到b右四位的值
#define LGET(b) ((LMASK&b) >> HALF_BITS_LENGTH)  //得到b左四位的值
#define GRIDW  3   // 九宫格边界长度
void fun()
{
unsigned char b;
for(LSET(b,1);LGET(b)<=GRIDW*GRIDW;LSET(b,(LGET(b)+1)))
{
for(RSET(b,1);RGET(b)<=GRIDW*GRIDW;RSET(b,(RGET(b)+1)))
{
if(LGET(b)%GRIDW!=RGET(b)%GRIDW)
{
cout<<"A = "<<LGET(b)<<","<<"B = "<< RGET(b)<<endl;
}
}
}
}


这个解法也是出题人的原本解法,通过一个8位的byte类型的值来保存两个变量的值,这个确实是绰绰有余,前四位保存一个A,后四位保存B值,通过移位运算,来获取A,B两个的真实值。对这个求余作比较,得出结果。想想这个解决办法,不由感慨一句,大神啊!!!没有强大的知识基础和编程功底,谁能想到把一个数分成两个来分别表示两个值呢?还能想到移位运算。确实厉害,没有一定的功底是没法想到这个解法的。

第二个解法:

void fun()
{
int i = 81;
while(i--)
{
if(i / 9 % 3 == i % 9 % 3)continue;
cout<<"A = "<<i / 9 + 1<<","<<"B = "<< i % 9 + 1<<endl;
}
}


看到这个解法,更让人叹为观止啊,想出第一种解法的人是大神,那想出这种解法的人那就是仙儿了,充分利用了“/”和“%”这两个运算符得到想要的结果,前提是必须深刻理解这两个运算符的含义,然后充分利用这两个运算符得出计算结果。虽然说有些偷工取巧了,但是确实解出来,也是非常简洁的一种解法。再感叹一声,仙儿!

第三种解法:

struct {
unsigned char a:4;
unsigned char b:4;
} i;
void fun()
{
for(i.a = 1; i.a <= 9;i.a++)

for(i.b = 1;i.b <=9;i.b++)

if(i.a % 3 != i.b % 3)

printf("A=%d,B=%d\n",i.a,i.b);
}


虽然这个解法也解出来了,但是我不太认同,这是一个变量吗,明明是一个结构体里面的两个变量,哎,不知道编程之美的编著者怎么会收录这个解法,不管正确不正确,写上去大家看看也行。

第四种解法:

void fun()
{

for(int i = 1;i <= 9;i++)
{
cout<<"A = "<<i<<","<<"B = "<<((i + 1)>=10?(i + 1 + 1):(i + 1)) % 10<<endl;
cout<<"A = "<<i<<","<<"B = "<<((i + 2)>=10?(i + 2 + 1):(i + 2)) % 10<<endl;
cout<<"A = "<<i<<","<<"B = "<<((i + 4)>=10?(i + 4 + 1):(i + 4)) % 10<<endl;
cout<<"A = "<<i<<","<<"B = "<<((i + 5)>=10?(i + 5 + 1):(i + 5)) % 10<<endl;
cout<<"A = "<<i<<","<<"B = "<<((i + 7)>=10?(i + 7 + 1):(i + 7)) % 10<<endl;
cout<<"A = "<<i<<","<<"B = "<<((i + 8)>=10?(i + 8 + 1):(i + 8)) % 10<<endl;
}
}


这个解法是我自己想出来的解法,有点笨,但是也是一种解法,利用了十进制的特点解决的,A每在一个位置,B都有六个位置存在,这六个位置是有特点的,根据这个特点计算出来的。

不管是那种解法,能解决就行,认真考虑这个题,我觉得就是一个数字游戏,看解题者能不能充分利用数字的特点和编程功底来实现,此题很不错,活跃思维。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: