中国象棋将帅问题

Posted yuyaweibest

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了中国象棋将帅问题相关的知识,希望对你有一定的参考价值。

中国象棋将帅问题

分析与解法:

程序的大体框架:
1.遍历 A 的位置
2. 遍历 B 的位置
3. 判断 A 、 B 的位置组合是否满足要求
如果满足,则输出。

类似于双层循环运算

创建一个逻辑坐标系统,用 1-9 的数字,按照行优先的顺序来表示每个格点的位置。

   关键问题在于只使用一个变量来表示A和B的位置。所以可以使用位运算来解决。一个无符号字符类型的长度是1字节,也就是8位,8位可以表示2^8=256个值,对于A、B的9个位置来说足够。可以用前4位来表示A的位置情况,后4位表示B的位置情况。而4位可以表示16个数也足够表示A、B的位置情况了。
   解法一给出的方法是将一个Byte变量拆成两个用,前一半代表“帅”可以走的位置,后一个变量代表“将”可以走的位置(事先已经将“将”和“帅”可以走的3*3的位置进行了编号),利用位操作即可获得两个计数器的功能。

通过位运算可以对A、B的位置进行读取和修改。
几种基本的位运算:
(1)& 按位与运算
(2)| 按位或运算 “与”和”或”就不用说了吧
(3)^ 按位异或运算 相同为假,不同为真
(4)~ 按位取反 一元运算符
(5)<< 按位左移 如 0000 0111 << 2 = 0001 1100,将此数左移两位相当于将此数扩大两倍。
(6)>> 按位右移 如 0001 1000 >> 2 = 0000 0110,将此数右移两位相当于将此数缩小两倍。
令LMASK为1111 0000,另任意一个1字节的字符型变量与其做与运算,结果右移四位,便可得到此变量的高四位的值。
Example,
0110 1011
&1111 0000
= 0110 0000 >> 4 = 0000 0110
同理,令RMASK为0000 1111,即可得到它低四位的值。
Ex.
0110 1011
& 0000 1111
= 0000 1011
设置1字节字符型变量,比如对高四位进行设置,先将变量与RMASK相与,将要修改的变量左移四位后于前一结果进行“异或”或“或运算”。
Ex.将0110 1011高四位设置为1001.
0110 1011
& 0000 1111
= 0000 1011 0000 1001 << 4 = 1001 0000
^ 1001 0000
= 1001 1011
同样的方法设置低四位的值。

解法1:
代码:

#include <stdio.h>
#include <iostream>
#define HALF_BITS_LENGTH 4
//这个值是记忆存储单元长度的一半,在这道题里是4bit
#define FULLMASK 255
//这个数字表示一个全部bit的mask,在二进制表示中,它表示11111111.
#define LMASK (FULLMASK<<HALF_BITS_LENGTH)
//这个数字表示左bits的mask,在二进制表示中,它是11110000.
#define RMASK (FULLMASK>>HALF_BITS_LENGTH)
//这个数字表示右bits的mask,在二进制表示中,它是00001111.
#define LSET(b,n) (b=(RMASK&b)|((n)<<HALF_BITS_LENGTH))
//这个宏,将b的左边设置成n
#define LGET(b) ((b&LMASK)>>HALF_BITS_LENGTH)
//这个宏,得到左边的值
#define RSET(b,n) (b=((LMASK&b)|(n)))
//这个宏,将b的右边设置成n
#define RGET(b) (RMASK&b)
#define GRIDW 3//这个数字表示将帅移动范围的行宽度
using namespace std;
int main ()

    unsigned char b;
    for (RSET(b,1);RGET(b)<=GRIDW*GRIDW;RSET(b,(RGET(b)+1)))
        for(LSET(b,1);LGET(b)<=GRIDW*GRIDW;LSET(b,(LGET(b)+1)))
            if((LGET(b)%GRIDW)!=(RGET(b)%GRIDW))
                cout<<"A= "<<RGET(b)<<" B= "<<LGET(b)<<"\\t";
    cin.get();
    return 0;

输出:

解法2:
用到了 C 语言的 struct 结构,原理和解法1类似
其中unsigned char a:4表示结构体中a的位域只有4位,高位用作它用。只能在结构体里使用,建议尽量少用,会破坏程序的移植性。
当结构体中的元素的取值范围很小时,可以将几个字段按位合成一个字段来表示,起到节省内存空间的作用。
代码:

#include <stdlib.h>
#include <iostream>
using namespace std;
struct
    unsigned char a:4;
    unsigned char b:4;
i;//一个struct类型占用一个字节
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%3)
            cout<<"A= "<<int(i.a)<<" B= "<<int(i.b)<<"\\t";//需将char型转化为int型
                //printf("A = %d, B = %d\\n", i.a, i.b);
cout<<endl<<sizeof(i);
cin.get();
return 0;

输出:

解法3:
可以把变量i想象成一个两位九进制的变量,而i在计算机中存储的值是i的十进制表示。则i/9的计算机处理结果,即结果直接去掉小数点后部分的结果即是此九进制数的第二位,而i%9即是此九进制数的个位。本程序用此九进制数的第二位保存A的位置,个位表示B的位置。九进制00-88对应十进制00-80。我们共需要验证9*9=81种位置关系,这也是i=81的由来。此外我们要明白i/9和i%9的含义。我们知道,整数i可以由部两分组成,即var=(var/9)*9+var%9 ,其中var

#include <iostream>
//#define BYTE unsigned char
typedef unsigned char BYTE;
using namespace std;
int main()

BYTE i=81;
while(i--)

    if(i/9%3!=(i%9%3))
        cout<<"A= "<<i/9+1<<" B= "<<i%9+1<<"\\t";

cin.get();
return 0;

输出:

参考:http://blog.csdn.net/kabini/article/details/2256421

以上是关于中国象棋将帅问题的主要内容,如果未能解决你的问题,请参考以下文章

清朝北洋时期的保定军校到底成就了多少将帅?

将帅问题

象棋各子走法

《编程之美》practice

位域 (Bit field)

BCZM: Chapter 1