中国象棋将帅问题(输出所有将帅的合法位置)
我们看一下怎么来看将和帅的位置,来比较她们两的位置是否会冲突

我们用数字1~9来标记棋子的位置然后进行比较A=将,B=帅
本题的难点其实是在
如何使用一个变量来保存这一个将,还有另一个帅的位置
第一种方法
那我们其实可以这样,我们可以使用一个8个比特的变量来保存这两个数据,前四个比特来保存A的位置,后四个比特来保存B的位置
四个比特刚好可以保存2^4=16个数字而我们的将和帅的位置刚好是有9个数字构成的所有我们用16个数字来保存绰绰有余
那么我们怎么用这一个8比特的变量分别用他的前后四比特来
分别保存两个值,然后又怎么将这两个值有分别从这一个变量中
取出来进行比较呢?
首先是将这两个将与帅的位置保存起来(b为前一次运算后的结果)
先保存B=1
首先清除结果右边的字节,为后面将B的位置保存做准备
0000 0000 (b)
&1111 0000 (LMASK) (
&运算有0即0
)
0000 0000 (b&LMASK)
再将得到的这个数字和我们即将要保存的B相或,就将B的位置保存下来了
0000 0000 (b&LMASK)
| 0000 0001 (B) (
|运算有1即1)
0000 0001 (b)
得到结果 00000001
再保存A=1
首先清除上一步结果中左边字节,为保存A的位置做准备
0000 0001 (b)
&0000 1111 (RMASK)
0000 0001 (RMASK&b)
再将A的位置保存进去
将A的位置保存之前我们先要将A<<4
0000 0001<<4=0001 0000 (A<<4)
再将A变化后的结果与我们运算后的b|起来
0001 0000 (A<<4)
|0000 0001 (b)
0001 0001 (b)
得到结果0001 0001,即位置A=1,B=1
总的来说
将这两个位置保存起来的话只需要一句话
(((b&LMASK)|B)&RMASK)|(A<<4)
再将这两个将与帅的位置取出进行比较(b为上一步保存后的结果,将这个结果保存起来,之后会用到)
先得到B的位置
0001 0001 (b)
&0000 1111 (RMASK)
0000 0001 (B的位置)
再得到A的位置
0001 0001 (b)
&1111 0000 (LMASK)
0001 0000
再将这个结果>>4即
0001 0000>>4=0000 0001 (A的位置)
在得到又取出A、B位置之后我们在进行位置的比较,之后输出符合条件的位置(符合条件的位置即AB的位置不能处于同一条直线上,
即将这个值%3若相同则在同一行)
然后我们来写代码
#define
_CRT_SECURE_NO_WARNINGS
1
#define
BITS
4
#define
FULLNUMBER
255
#define
LMASK
(
FULLNUMBER
<<
BITS
)
//1111 0000
#define
RMASK
(
FULLNUMBER
>>
BITS
)
//0000 1111
#define
RSET
(b,n) (b=((b&
LMASK
)^n))
//将b的右边设置成n
#define
LSET
(b,n) (b=((b&
RMASK
)^(n<<
BITS
)))
//将b的左边设置成n
#define
RGET
(b) (b&
RMASK
)
//得到b右边的值
#define
LGET
(b) ((b&
LMASK
)>>
BITS
)
//得到b右边的值
#include<stdio.h>
int
main()
{
unsigned
char
b = 0;
for
(
LSET
(b, 1);
LGET
(b) <= 9;
LSET
(b, (
LGET
(b) + 1)))
{
for
(
RSET
(b, 1);
RGET
(b) <= 9;
RSET
(b, (
RGET
(b) + 1)))
{
if
(
RGET
(b) % 3 !=
LGET
(b) % 3)
{
printf(
"A=%d,B=%d\t\t"
,
LGET
(b),
RGET
(b));
}
}
}
system("pause");
return
0;
}
结果呢是这样的

第二种方法
因为将与帅问题的位置共有9*9=81种结果,我们就要在这81种结果中找出符合条件的位置打印出来
那么我们拥有的这个变量只要可以保存81个这么多的变量就可以了
那我们还是用unsigned char型变量有8字节,最大可以保存255种情况
(书上用的是BYTE,BYTE在VC中就表示的是unsigned char,但是在VS中没有这个变量类型)
先看代码
#define
_CRT_SECURE_NO_WARNINGS
1
#include
<stdio.h>
int
main()
{
unsigned
char
i = 81;
while
(i--)
{
if
(i / 9 % 3 == i % 9 % 3)
{
continue
;
}
printf(
"A=%d,B=%d\t\t"
, i / 9 + 1, i % 9 + 1);
}
system(
"pause"
);
return
0;
}
其实这个方法呢就是利用数学的方法,例如73-81
i/9就一直是8,i%9即1-8,就刚好是将与帅所对应的位置
再%3运算之后进行比较,将符合条件的打印
这个方法真的特别巧妙,理解理解重在理解
第三种方法
第三种方法是在第一种方法上做的改进,也使用位运算,但是我们使用了一个位段的结构,刚好是按byte来保存数据
#define
_CRT_SECURE_NO_WARNINGS
1
#include
<stdio.h>
struct
{
unsigned
char
a : 4;
unsigned
char
b : 4;
}i;
//位段名字
int
main()
{
for
(i.a = 1; i.a <= 9; i.a++)
for
(i.b = 1; i.b <= 9; i.b++)
{
if
(i.a % 3 != i.b)
{
printf(
"A=%d,B=%d\t\t"
, i.a, i.b);
}
}
system(
"pause"
);
return
0;
}
这个方法是直接利用位段,直接按位来保存数据,按位来读取数据,然后再进行比较,符合条件的直接打印,这个方法其实也比较简单
综上所述,其实第二种方法最简单,但是及其不容易想到,第三种其实也比较简单,不过要是对位段不熟悉的话也写不出来,第一种呢是最容易想到的,但是操作起来及其麻烦,而且稍有不慎就会做错,这得需要对位运算十分了解才可以,并且
在使用宏的时候一定要小心运算符的优先级,如果对这个不大清楚的话那么你就直接加括号,括号千万不要少,要记住括号是优先级的死敌