编程之美1.2中国象棋将帅问题

今天认真把中国象棋将帅问题看了一遍,确实如其它同学所说—不难,只要自己理解了其中的思想,其他的只是实现方法不一样而已。下面,我也捋一捋自己的想法。
其实分析到用九个数字表示格点位置是关键,这样只需要用取余运算就可判断A,B是否互斥。
[例如:位置1和位置4对3取余后都是1,则两者互斥。]
此时,我们只需遍历两个不同的格子位置,例如:

for(i=1;i<=9;i++){
        for(j=1;j<=9;j++){
            if((i%3) != (j%3))//记得要取余后作比较
                printf("i=%d,j=%d",i,j);
        }
    }

可是题目的要求是只能用一个变量,而上面的用了两个变量,这是此题的关键点。当我们用bit来表示时,一个8位的byte类型能够表示2^8=256个值,将之分为2段,前4bit表示A的位置,后4bit表示B的位置,而每4bit可以表示2^4=16个值,这已经足够。那么怎么实现前4bit表示A,后4bit表示B呢?当然是采用位运算了。

取n=0110 1011,得到其高四位与低四位的值:
0110 1011
&
1111 0000
= 0110 0000 >> 4 = 0000 0110(高四位)
同理,令RMASK为0000 1111,即可得到它低四位的值。
0110 1011
&
0000 1111
= 0000 1011(低四位)

将0110 1011高四位设置为1001.
0110 1011
&
0000 1111= 0000 1011
0000 1001 << 4 = 1001 0000,再与0000 1011做或运算
0000 1011
^
1001 0000= 1001 1011
同样的方法设置低四位的值。
代码:

int main(void){
BYTE b; //只有一个变量
GRIDW=3;
for(LSET(b, 1); LGET(b) <= GRIDW * GRIDW; LSET(b, (LGET(b) + 1)))
//从A的1位置一直找到9位置,注意自增的写法(高4位)
//相当于for(i=1;i<9;i++)
for(RSET(b, 1); RGET(b) <= GRIDW * GRIDW; RSET(b, (RGET(b) + 1)))
//从B的1位置找到9位置(低4位)
//相当于for(i=1;i<9;i++)
if((LGET(b) % GRIDW) != (RGET(b) % GRIDW))
//如果不在同一列,则打印结果
printf(“A = %d, B = %d\n”, LGET(b), RGET(b));
return 0;
}

思路很简单,用高4位表示A,低4位表示B,但是实现起来有点复杂,于是出现了解法二。就如同前面用两个for循环来遍历是最简单的了,因为只用一个变量的限制而作废,那么有没有什么方法实现和for循环一样的功能呢?
看程序:

int main(void){
    BYTE i = 81;
    while(i--){
        if((i / 9) % 3 == (i % 9) % 3)//重点
            continue;
        printf("A = %d, B = %d\n", i /9 + 1, i%9 + 1);
    }
    return 0;
}

重点在if语句,(i/9)与(i%9)的区别,当i取73—81时,i–后,i/9=8,而i%9={0,1,2,3,4,5,6,7,8},i取64—72时,i–后,i/9=7,而i%9={0,1,2,3,4,5,6,7,8},太惊奇了,一个变量实现了内外双层循环的效果。
这就够了吗?只有你想不到,没有大牛想不到的。
当然还有还有效率更高的算法:

    int main(void){
    struct{
        unsigned char a:4;
        unsigned char b:4;
    }i;
    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;
}

其实是运用了结构体与位段,结构体里的对象是高低四位,其中高四位表示a,低四位表示b(按编译器不同,也可能相反),定义一个变量i,通过直接应用进行比较,也是对两个for循环进行改进。
综上所述,两种思路。思路一:运用位运算来表示A,B;思路二:运用两个for循环来遍历,但需要改进。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值