面试题:中国象棋将帅问题

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

我们用数字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;
}
这个方法是直接利用位段,直接按位来保存数据,按位来读取数据,然后再进行比较,符合条件的直接打印,这个方法其实也比较简单


综上所述,其实第二种方法最简单,但是及其不容易想到,第三种其实也比较简单,不过要是对位段不熟悉的话也写不出来,第一种呢是最容易想到的,但是操作起来及其麻烦,而且稍有不慎就会做错,这得需要对位运算十分了解才可以,并且 在使用宏的时候一定要小心运算符的优先级,如果对这个不大清楚的话那么你就直接加括号,括号千万不要少,要记住括号是优先级的死敌

猜你喜欢

转载自blog.csdn.net/qq_36767247/article/details/80628357
今日推荐