任务难度:3
任务说明:
在中国象棋里,当只剩下将帅(A和B代指将和帅)两个棋子时 将帅不能处于同一列 要求输出将帅的所有合法位置(将帅所在的九宫格从上到下从左至右编号为1~9)
例如:
合法位置:
A=1,B=2
不合法位置:
A=1,B=1
要求:只能使用一个变量
乍一看问题并不难,但是最后的要求是只能使用一个变量,这也是好玩之处~~
先让我们思考一下A和B的状态共有多少种,九宫格的9个位置,即各有9种状态,9需要用4bit来存储,储存AB两种状态则至少需要8bit,即一个BYTE
那么主体思想就是我们通过某种循环方式,分别枚举AB状态的所有值(1~9)当两个状态的%3即为两个状态的列数相同时,则不能输出,否则输出
接下来介绍三种方法,让你学会位运算:
方法一:
假设我们开一个BYTE型的变量b,用左边的4bit存储B的状态,用右边的4bit存储A的状态,我们需要实现4种操作,即取出该变量的左边四位或右边四位,将该变量的左边四位或右边四位设置为n值(n取1~9)
取出右边的值只需要将变量b与00001111(RMASK)相与即可,取出左边四位的值只需要先将b与11110000(LMASK)相与,再右移四位即可
而将右边的值改为n 就需要将左边的值先保留下来 即将变量b与11110000相与,然后再和n异或即可
而将左边的值改为n 就需要将右边的值先保留下来,即将变量b与00001111相与,然后再和右移四位后的n异或即可
我们用四个宏定义的函数实现上述功能,循环枚举的时候只需要先将b的左右置为1,然后每次多置1即可,代码如下:
#include <cstdio>
#include <windows.h>
/*解法一*/
#define HALF_BIT_LENGTH 4 //因为要频繁使用左右移4位的操作,这里把4宏定义出来
#define FULLMASK 255 //即11111111 用于得出LMASK和RMASK
#define LMASK (FULLMASK<<HALF_BIT_LENGTH) //将11111111左移4位 即得到11110000
#define RMASK (FULLMASK>>HALF_BIT_LENGTH) //即00001111
#define RGET(b) (b & RMASK) //得到右4位
#define LGET(b) ((b& LMASK)>>HALF_BIT_LENGTH) //得到左4位
#define RSET(b,n) (b= (b & LMASK)^n) //将右四位置为n
#define LSET(b,n) (b= (b & RMASK)^(n<<HALF_BIT_LENGTH)) //将左四位置为n
#define GRIDW 3 //由于求列数时需要对3取余,这里也宏定义出来
int main()
{
unsigned char b;
for (RSET(b,1);RGET(b)<=GRIDW*GRIDW;RSET(b,(RGET(b)+1)))
{//枚举右四位从1到9的情况,即枚举A的9种状态
for (LSET(b,1);LGET(b)<=GRIDW*GRIDW;LSET(b,(LGET(b)+1)))
{//枚举左四位1到9的情况,即枚举B的9种状态
if (RGET(b)%GRIDW!=LGET(b)%GRIDW)//列数不同则表示AB位置合法
{
printf("A=%d, B=%d\n",RGET(b),LGET(b));
}
}
}
return 0;
}
是不是感觉很奇妙呢?这就是位运算的妙用,接下来介绍两种更为简便的方法
方法二:我们将AB的所有状态排列组合,其实只有81种组合方式,我们直接定义一个BYTE变量i表示AB位置的排列方式,i=1表示A为1 B为1
用i/9+1表示A i%9+1表示B i的取值范围为1~81 想一想这样是不是涵盖了A与B分别取1~9的81种情况
代码如下:
#include <cstdio>
#include <windows.h>
BYTE i=0;
int main()
{
for (;i<=81;i++)
{
if ( i/9%3 != i%9%3)
{
printf("A=%d, B=%d\n",i/9+1,i%9+1);
}
}
return 0;
接下来介绍最后一种方法 最简单也最容易理解 直接创建一个大小为BYTE的struct变量 将BYTE的前4位和后4位分开直接设为两个成员变量
代码如下:
#include <cstdio>
#include <windows.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%3)
{
printf("A=%d, B=%d\n",i.a,i.b);
}
}
}
return 0;