编程之美:第一章 1.2 中国象棋将帅问题

/*
中国象棋将帅问题:
自古将帅不能照面
                __ __ __
10              将
9
8
7
6
5
4
3
2
1                       帅
 a b c d e f g h i
A表示将,B表示帅,A被限制在{d10,f10,d8,f8}中,B被限制在{d3,f3,d1,f1}中。
每一步A,B可以横向或纵向移动一个。A与B不能在同一条纵向直线上,比如A在d10位置,B就不能在d1,d2,d3位置

请写出一个程序,输出A、B所有合法位置。要求在代码中只能使用一个字节存储变量。
起始就是一个全排列问题

算法:
遍历A的位置
 遍历B的位置
  判断A、B的位置组合是否满足要求
  如果满足则输出

需要存储的是A、B的位置信息,并且每次循环需要更新。
创建一个逻辑坐标系统,用1~9,按照行优先的顺序表示每个格点的位置。这样用模运算可以得到当前的列号,
判断A和B是否互斥。

只用一个变量,存储A和B的两个子的位置信息。
因此只能从位上做文章,用字节8位,256个值,足够表示A、B的位置信息。
因此将该字节一分为而,前4位表示A,后4位表示B,这样每个棋子都有16种位置表示方法,已经足够(牛逼啊)

将byte b(10100101)的右边4位(0101)设为n(0011)
1先清除b右边的bits,用11110000
                    & 10100101
    得到  10100000
        | 00000011
    得到  10100011

2将byte b(10100101)的左边4位(1010)设为(0011)
清除左边的4bit,同时保存右边的4bit,用与
                      00001111
      &10100101
    得到  00000101
    将n 0011左移4位
         n << 4 = 00110000
      做或运算
      00000101
    |    00110000
    得到 00110101

3将得到的byte的数据的右边4位或左边4位(将10100101中的1010及0101)
清除b左边的bits,同时保持右边的bits
                  00001111
    & 10100101
      得到  00000101
清除右边的bits,同时保持左边的bits
     11110000
    &10100101
   得到 10100000
 结果右移4位  10100000 >> 4 = 00001010

如何在不声明其他变量约束的前提下创建一个for循环。可以重复利用1byte存储单元,把它作为循环计数器
并用前面提到的存取和读入技术进行判断。
还可以用宏来抽象代码。
for( LEST(b,1) ; LEST(b) <= GRIDW * GRIDW ; LSET(b,(LGET(b) + 1)))
*/

/*
关键:
1 因此将该字节一分为二,前4位表示A,后4位表示B,这样每个棋子都有16种位置表示方法,已经足够(牛逼啊)
2 算法:
遍历A的位置
 遍历B的位置
  判断A、B的位置组合是否满足要求
  如果满足则输出
3 #define LEFT_MASK (FULL_MASK << 4) //注意,用define定义东西的时候,若有两个变量,需要加括号
4 #define RIGHT_MASK (FULL_MASK >> 4) //右掩码就是右边4位全是1
5 #define LSET(b,n) (b = ((b & RIGHT_MASK) | ((n) << 4) ) )//将比特b的左边设定为n,主要为循环遍历使用。采用的方法是,
//左边清零(通过与右掩码(00001111)相与即可),然后将数n向左移4位,然后将上述两个数用或即可,注意别漏了赋值操作
//注意n要用括号括起来,他表示一个数,不是一个字符
6 #define LGET(b) ( (b & LEFT_MASK) >> 4)//获取比特b的左边4位的方法是,先将右边4位清零,
//再右移4位即可
7    if(LGET(b) % STEP != RGET(b) % STEP)//牛逼,用九宫格模拟两者取模之后的余数不同,就表示
    //不在一条竖线上就可以
*/

#include <stdio.h>
#define FULL_MASK 255
//#define HALF_MOVE 4
#define LEFT_MASK (FULL_MASK << 4) //注意,用define定义东西的时候,若有两个变量,需要加括号
#define RIGHT_MASK (FULL_MASK >> 4) //右掩码就是右边4位全是1
#define LSET(b,n) (b = ((b & RIGHT_MASK) | ((n) << 4) ) )//将比特b的左边设定为n,主要为循环遍历使用。采用的方法是,
//左边清零(通过与右掩码(00001111)相与即可),然后将数n向左移4位,然后将上述两个数用或即可,注意别漏了赋值操作
//注意n要用括号括起来,他表示一个数,不是一个字符
#define RSET(b,n) (b = ((b & LEFT_MASK) | (n) ) )//将比特b的右边设定为n,采用的方法是:
//右边清零(将该数与左掩码(11110000)相与),然后与该数n相或
#define LGET(b) ( (b & LEFT_MASK) >> 4)//获取比特b的左边4位的方法是,先将右边4位清零,
//再右移4位即可
#define RGET(b) (b & RIGHT_MASK)//获取比特b的右边4位的方法是,将该数右边4位与右掩码相与即可
#define STEP 3

void ChineseChess_define()
{
 unsigned char b;
 int iCnt = 0;
 for(LSET(b,1) ; LGET(b) <= STEP * STEP ; LSET(b,(LGET(b) + 1) ) )
 {
  for(RSET(b,1) ; RGET(b) <= STEP * STEP ; RSET(b,(RGET(b) + 1) ) )
  {
   if(LGET(b) % STEP != RGET(b) % STEP)//牛逼,用九宫格模拟两者取模之后的余数不同,就表示
    //不在一条竖线上就可以
   {
    printf("A=%d,B=%d\n",LGET(b),RGET(b));
    iCnt++;
   }
  }
 }
 printf("%d\n",iCnt);
}

void ChineseChess_mod()
{
 unsigned char i = 81;//?BYTE是什么类型
 int iCnt = 0;
 while(i--)
 {
  if(i / 9 % 3 == i % 9 % 3)//?不懂
  {
   continue;
  }
  printf("A=%d,B=%d\n",i / 9 + 1 , i % 9 + 1);
  iCnt++;
 }
 printf("%d\n",iCnt);
}

typedef struct Num
{
 unsigned char a:3;
 unsigned char b:4;
}Num;
void ChineseChess_struct()
{
 int iCnt = 0;
 Num Num1;
 for(Num1.a = 1 ; Num1.a <= 9 ; Num1.a++)
 {
  for(Num1.b = 1 ; Num1.b <= 9; Num1.b++)
  {
   if(Num1.a % 3 != Num1.b % 3)
   {
    printf("A=%d,B=%d\n",Num1.a,Num1.b);
    iCnt++;
   }
  }
 }
 printf("%d\n",iCnt);
}

void process()
{
 ChineseChess_define();
 //ChineseChess_mod();
 //ChineseChess_struct();//似乎有错误
}

int main(int argc,char* argv[])
{
 process();
 getchar();
 return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值