中国象棋将帅问题

中国象棋将帅问题:
 
 

  
  
  
  

 
在一把象棋的残局中,象棋双方的将帅不能够相见,即不能够在中间没有其余棋子的状况下在同一列出现。而将、帅各被限制在己方的3*3的格子中运动。相信你们都很是熟悉象棋的玩法吧,这里就不详细说明游戏规则了。 用A、B表明将和帅,请写出一个程序,输出A、B全部合法的位置。要求在代码中只能用一个变量。
分析与解法:
这个问题的解法并不复杂。
遍历A的全部位置
遍历B的全部位置
若是A的位置和B的位置在同一列
输出结果
不然 继续寻找
地图能够用0-8表示A或B可能的9个位置
0------1------2
3------4------5
6------7------8
关键问题在于只使用一个变量来表示A和B的位置。因此可使用位运算来解决。一个无符号字符类型的长度是1字节,也就是8位,8位能够表示2^8=256个值,对于A、B的9个位置来讲足够。能够用前4位来表示A的位置状况,后4位表示B的位置状况。而4位能够表示16个数也足够表示A、B的位置状况了。
经过位运算能够对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>
  2. #define HALF_BITS_LENGTH 4 //一半字节的长度
  3. #define FULLMASK 255 //即1111 1111
  4. #define LMASK (FULLMASK << HALF_BITS_LENGTH) //即1111 0000
  5. #define RMASK (FULLMASK >> HALF_BITS_LENGTH) //即0000 1111
  6. #define RSET(b, n) (b = (LMASK & b) ^ n) //设置低四位
  7. #define LSET(b, n) (b = (RMASK & b) ^ (n << HALF_BITS_LENGTH)) //设置高四位
  8. #define RGET(b) (RMASK & b) //获得低四位
  9. #define LGET(b) ((LMASK & b) >> HALF_BITS_LENGTH) //获得高四位
  10. #define GRIDW 3 //将帅活动范围尺寸
  11. #define BYTE unsigned char
  12. int main(void)
  13. {
  14. BYTE b; //只有一个变量
  15. for(LSET(b, 1); LGET(b) <= GRIDW * GRIDW; LSET(b, (LGET(b) + 1)))//从A的1位置一直找到9位置,注意自增的写法
  16. for(RSET(b, 1); RGET(b) <= GRIDW * GRIDW; RSET(b, (RGET(b) + 1)))//从B的1位置找到9位置
  17. if((LGET(b) % GRIDW) != (RGET(b) % GRIDW)) //若是不在同一列,则打印结果
  18. printf("A = %d, B = %d\n", LGET(b), RGET(b));
  19. return 0;
  20. }
这是个关于如何利用位运算解决问题的一个简单的运用,能够看到位运算合理地利用一个变量解决象棋将帅问题。算法自己很简单,重点是位运算的应用。
<BOP>上还有两个更简洁的算法:
第一个:
   
   
  
    
    
    
    
  
   
  1. #include<stdio.h>
  2. #define BYTE unsigned char
  3. int main(void)
  4. {
  5. BYTE i = 81;
  6. while(i--)
  7. {
  8. if((i / 9) % 3 == (i % 9) % 3)
  9. continue;
  10. printf("A = %d, B = %d\n", i /9 + 1, i%9 + 1);
  11. }
  12. return 0;
  13. }
能够把变量i想象成一个两位九进制的变量,而i在计算机中存储的值是i的十进制表示。则i/9的计算机处理结果,即结果直接去掉小数点后部分的结果便是此九进制数的第二位,而i%9便是此九进制数的个位。本程序用此九进制数的第二位保存A的位置,个位表示B的位置。最大值为88,即为十进制的80.程序从十进制的80,即九进制的88遍历到十进制的0,即九进制的0.将符合条件的位置所有输出。
第二个:
   
   
  
    
    
    
    
  
   
  1. #include<stdio.h>
  2. int main(void)
  3. {
  4. struct
  5. {
  6. unsigned char a:4;
  7. unsigned char b:4;
  8. }i;
  9. for(i.a = 1; i.a <= 9; i.a++)
  10. for(i.b = 1; i.b <= 9; i.b++)
  11. if(i.a % 3 != i.b % 3)
  12. printf("A = %d, B = %d\n", i.a, i.b);
  13. return 0;
  14. }
算法与上面的一模一样。
其中unsigned char a:4表示结构体中a的位域只有4位,高位用做它用。只能在结构体里使用,建议尽可能少用,会破坏程序的移植性。
当结构体中的元素的取值范围很小时,能够将几个字段按位合成一个字段来表示,起到节省内存空间的做用。
Ex:
   
   
  
    
    
    
    
  
   
  1. #include<stdio.h>
  2. int main(void)
  3. {
  4. struct test
  5. {
  6. unsigned char a:4;
  7. unsigned char b:4;
  8. }i;
  9. i.a = 15;
  10. i.b = 10;
  11. printf("%d\n", sizeof(i));
  12. }
将上面例子中的变量i的大小输出,结果为1字节。说明i.a和i.b各占4位。
结构体是C语言中的一种经常使用的自定义数据结构。
看下面的例子:
   
   
  
    
    
    
    
  
   
  1. #include<stdio.h>
  2. int main(void)
  3. {
  4. struct test
  5. {
  6. int a;
  7. char b;
  8. }i;
  9. printf("%d\n",sizeof(i));
  10. }
按理说结构体变量i的大小应该是sizeof(int)+sizeof(char),即5,而输出显示的结果为8。再看一个例子:
   
   
  
    
    
    
    
  
   
  1. #include<stdio.h>
  2. int main(void)
  3. {
  4. struct test
  5. {
  6. int a;
  7. char b,c;
  8. }i;
  9. printf("%d\n",sizeof(i));
  10. }
应该是6对吧?结果仍是8.这是为何呢?
这是由于在32位的操做系统上,操做系统组织数据是以32位(4个字节)做为一个标准,所以各类变量的size都通常都是4的倍数。并且结构体数据都是按照定义时所使用的顺序存放的,所以在第一个例子中尽管b变量只会占有一个字节,可是a + b = 5 > 4,所以第一个4个字节存放a,第二个4个字节用于存放b,这样实际上就浪费了3个字节。在第二个例子中第二个4个字节用来存放b和c。
因此,在结构体中要注意结构体中的变量定义的顺序,不一样的顺序可能会形成占用空间的不一样。这在嵌入式程序设计等系统资源比较少的状况下尤其重要。好比以下两种结构体:
   
   
  
    
    
    
    
  
   
  1. #include<stdio.h>
  2. struct m
  3. {
  4. char a;
  5. int b;
  6. char c;
  7. }x;
  8. struct n
  9. {
  10. char a;
  11. char c;
  12. int b;
  13. }y;
  14. int main(void)
  15. {
  16. printf("m:%d\nn:%d\n", sizeof(x), sizeof(y));
  17. return 0;
  18. }
对于结构体m来讲,x变量的大小为12,而y变量的大小为8.编译器是按程序员在结构体中声明变量的顺序处理的。不当的顺序会形成空间的浪费。读者能够想到发生这样状况的缘由的。因此建议声明结构体时,按照不一样变量的类型,按占用空间的大小升序或降序声明会取得较好的空间占用。
相关文章
相关标签/搜索