1.中国象棋将帅问题

来源:互联网 发布:gcp网络培训 编辑:程序博客网 时间:2024/03/29 00:31

问题描述:感觉有点像简化版的八皇后问题

在一把象棋的残局中,象棋双方的将帅不可以相见,即不可以在中间没有其他棋子的情况下在同一列出现。而将、帅各被限制在己方的3*3的格子中运动。相信大家都非常熟悉象棋的玩法吧,这里就不详细说明游戏规则了。用A、B代表将和帅,请写出一个程序,输出A、B所有合法的位置。要求在代码中只能用一个变量。如下图。


问题是可以简化为对于两个数组的互斥情况检查,互斥条件为两个位置不能同时出现在同一列,这个使用二位数组的行优先利用求模操作可以得到列号(整除操作可以得到行号),如果互斥条件不满足,则打印出合法位置,否则不合法。

但难点在于,只允许使用一个字节来实现全部的存储,这点就难了。在这里就要使用位存储,位运算的方式来实现了。发现一篇很好的位运算学习文章:点击打开链接


使用位操作处理整个问题的思路如下:

------------------

for A棋子出现的所有可能位置for B棋子出现的所有可能位置if(位置不冲突) print(A位置, B位置)

------------------

其中冲突条件为二者在同一列。

##########################################

按照常规思路,A棋子和B棋子至少各需要一个整型变量表示其位置,2个循环体里各需要一个整型变量使得循环进行下去,这样至少需要4个int型(16字节),不符合题目要求。所以首先需要解决这个空间问题。

首先两个相对位置必须要保存,1个字节有8位,如果掰成2半,一个有4位,由于每个棋子有3*3=9种可能的位置,所以四位(可记录16个数)完全够用了。按照行优先取模得列号得做法,使用(A的index)%3==(B的index)%3就可以作为循环体内的判断条件了。

现在解决for循环的信号变量的问题,如何做到不占用多余的空间来使得程序进行下去呢?

注意到两个嵌套的for循环都是从1开始,依次遍历棋子的9种可能位置,因此这和信号变量的自增是同步的,因此可以重复利用A,B的index信息来推动循环不断往下做。

##########################################

1.那么首先需要比较第一个位置,这就需要把字符的左边设成1,而右边4位不变->得到问题

如何设置某字符(b,假设是10100101)的左4位,如设为0011?

10100101&00001111=00000101

00000101|(00000101<<4)=00110101,

总结一下就是 ((b&00001111)|(n<<4))

类似设置右边四位就是 (b&00001111)|n)

2.如何得到某个棋子的位置信息来比较呢?->得到问题

如何将字符b的左四位或者右四位取出?

非常简单,取右边四位只需使用“00001111”相与即可,如

b=00110101

& 00001111

= 00000101

类似,取左边四位即是(b&11110000>>4)即可

3. 如何使用位运算来表示两个棋子的冲突情况?

两个棋子以行优先的方式表示,只需对行数(3)取模,如果得到的值相同,就是冲突的。

即左边的(b&00001111)%3==(b&11110000>>4)%3,则冲突


代码使用了一系列宏定义的方式使得代码看上去更简洁

#include <stdio.h>#define HALF_BITS_LENGTH 4#define FULL_MASK 255#define LMASK (FULL_MASK<<HALF_BITS_LENGTH) //11110000#define RMASK (FULL_MASK>>HALF_BITS_LENGTH)//00001111#define RSET(b,n) (b=((LMASK&b)|(n)))//设置b右边4位的值为n#define LSET(b,n) (b=((RMASK&b)|((n)<<HALF_BITS_LENGTH)))//设置b左边4位的值为n#define LGET(b) ((LMASK&b)>>HALF_BITS_LENGTH)// 得到b左边4位的值#define RGET(b) (RMASK&b)// 得到b右边4位的值#define GRIDW 3// 行动的范围int main(int argc, char const *argv[]){//printf("hello");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) printf("A=%d, B=%d\n",LGET(b), RGET(b));    return 0;}
编译一下得到这样的结果。

输出结果:


0 0
原创粉丝点击