编程之美:中国象棋将帅问题(开篇)

来源:互联网 发布:js获取系统当前日期 编辑:程序博客网 时间:2024/05/10 07:03

这学期打算准备面试,不过我不是科班出身,学的东西也比较杂乱,估计只能在数据结构上突出点优势了。因此打算把MS出版的那本编程之美研读一下,这是我学习的笔记。欢迎讨论和交流。
问题引入:假设一副棋盘上只有“将”和“帅”两个棋子。如下图所示:
这里写图片描述
为了方便,这里我用A表示“将”,B表示“帅“。现在的问题是:请写一个程序,输出所有A,B合法的位置(即A和B不能同时位于一条直线上),要求在代码中只能用一个字节存储变量。
其实问题思路不难,无非就是:
遍历A的位置
  遍历B的位置
    判断A,B的位置是否符合要求
    如果符合要求,则输出
程序比较难实现的地方时要求代码中只能用一个字节来存储变量。在C/C++中占据一个字节的变量类型只有bool和char,而这两个类型中bool只有true和false两个状态,但是char类型倒是可以做扩展。取值范围是0-255。
由于“将”和“帅”能移动的合法位置都是9个,所以最多有81种可能,所以用一个字节的左四位和右四位来分别表示“将”和“帅”
书上首先是给出了一种通过宏替换和移位运算实现的解法,不过对于我而言,估计我是怎么都想不出来他这种解法的。不过看了之后,对理解宏和移位运算帮助还是很大的。代码如下:

#include <iostream>#define FULLMASK 255//Max value of type char#define HALF_BITS_LENGTH 4// 记忆存储单元长度的一般 #define LMASK (FULLMASK<<HALF_BITS_LENGTH)//把max value左移4位,得到1111 0000#define RMASK (FULLMASK>>HALF_BITS_LENGTH)//把max value右移4位,得到0000 1111#define LSET(b,n) (b=((RMASK & b) | ((n) << HALF_BITS_LENGTH)))//把b的左边设置为N#define RSET(b,n) (b=((LMASK & b) | (n)))//把b的右边设置为N#define RGET(b) (RMASK & b)//得到b右边的值#define LGET(b) ((LMASK & b) >> HALF_BITS_LENGTH)//得到b右边的置#define MOVE_WIDTH 3//表示将帅移动范围的宽度int main(void){    unsigned char b;    for(LSET(b,1);LGET(b)<=MOVE_WIDTH*MOVE_WIDTH;LSET(b,  (LGET(b)+1)))       for(RSET(b,1);RGET(b)<=MOVE_WIDTH*MOVE_WIDTH;RSET(b,(RGET(b)+1)))       if(LGET(b)%MOVE_WIDTH!=RGET(b)%MOVE_WIDTH)           std::cout<<"将:"<<LGET(b)<<"\t"<<               "帅:"<<RGET(b)<<std::endl;      return 0;}

OK,以上就是全部的代码。这种方法虽然思路上和接下来介绍的两种没什么差别,不过对编程功力和对语言的了解要求挺高的。笨的方法虽然笨,不过对脑子的要求是很高的。
有句话我在知乎上看到过,不过记不清了。大概意思是说:方程的出现毫无疑问的是简化了人们的计算,不过当没有方程的时候你所做的运算,每一步都必须精心打造和计算,耗费的脑力是不亚于设置”方程“这种工具的。不过如果你多花点时间,设计一个好工具,会对你解决问题的效率有很大的提升。

下面为大家介绍两种效率比较高的解题方案:

#include <iostream>int main(void){    unsigned char i=81;    while(i--){        if(i/9%3==i%9%3)            continue;        std::cout<<i/9+1<<"\t"<<i%9+1<<std::endl;    }    return 0;}

不过貌似下面一种解法比这个更直接,不过时间复杂度貌似都是一样的。

#include <iostream>struct {    unsigned char a:4;    unsigned char b:4;}i;int main(void){    for(i.a=1;i.a<=9;i.a++)        for(i.b=1;i.b<=9;i.b++)            if(i.a%9!=i.b%9)                std::cout<<i.a<<"\t"<<i.b<<std::endl;

以上,错误此处,欢迎指出 。

0 0