[编程之美] PSet1.2 中国象棋将帅问题

来源:互联网 发布:java hdfs jar包 编辑:程序博客网 时间:2024/05/10 00:29

//象棋问题的解法//使用val变量左四位总共16个可取值代表A可能的位置(只需取0-8)//使用val变量右四位总共16个可取值代表B可能的位置(只需取0-8)#define GRDW 3int val;for(GETL4(val)=0 ; GETL4(val)<GRDW*GRDW ; GETL4(val)++)    for(GETR4(val)=0 ; GETR4(val)<GRDW*GRDW ; GETR4(val)++){        if(GETL4(val)%GRDW == GETR4(val)%GRDW)            continue;        printf("A=%d , B=%d" , GETL4,GETR4);    }
上面是伪代码,下面要考虑的是如何去获取左四位和右四位。并考虑如何在不声明变量的前提下将val的左四位和右四位自增
对于取左四位:
#define GetL4(val) ((val&1111 0000) > 4)
#define GetR4(val) (val&0000 1111)
考虑到11110000与00001111默认在编译器中以10进制数的形式存在,因此应该向如下定义:

#define HALF_BITS_LEN 4#define FULLMASK 255#define LMASK (FULLMASK << HALF_BITS_LEN) //1111 0000#define RMASK (FULLMASK >> HALF_BITS_LEN) //0000 1111#define GETL4(val) ((val & LMASK) >> HALF_BITS_LEN)#define GETR4(val) ((val & RMASK))

那么如何让val的左四位和右四位分别自加呢?可以定义以下操作:
#define SETL4(val,n) ((val & RMASK) | (n << HALF_BITS_LEN))#define SETR4(val,n) ((val & LMASK) | n)
这样可以将伪代码转换为正式代码如下:
for(SETL4(val,1) ; GETL4(val)<GRDW*GRDW ; SETL4(val,(GETL4(val)+1)))    for(SETR4(val,1) ; GETR4(val)<GRDW*GRDW ; SETR4(val,(GETR4(val)+1)))        if(GETL4(val)%GRDW == GETR4(val)%GRDW)            continue;        printf("A=%d , B=%d \n" , GETL4(val) , GETR4(val));

解法二:
思想与解法一一样,不过使用了struct的存储思想,会破坏程序的可移植性,建议少用。
struct BYTE{    unsigned char a:4;    unsigned char b:4;}val;for(val.a=0 ; val.a<9 ; val.a++)    for(val.b=0 ; val.b<9 ; val.b++)        if(val.a%3 != val.b%3)            printf("A=%d , B=%d \n" , val.a , val.b);

struct m{    char a;    int b;    char c;}x;struct n{    char a;    char c;    int b;}y;int main(void){    printf("m:%d\nn:%d\n", sizeof(x), sizeof(y));    return 0;}
结果m为12字节,n为8字节。

解法三:(最漂亮的方法---数学之美)
int val=81;//下标从0开始,循环81次,从80-0while(val--){    if(val/9%3 !=val%9%3)        printf("A=%d , B=%d \n" , val/9 , val%9);}
本例中使用到的思想:
试想一个有81个元素val(0-80),行列标号均为0-8,这样可以通过遍历val的值,val/9可以得到行号--代表A位置,val%9可以得到列号--代表B位置。通过碰撞检测完成输出。

扩展思考

我们注意到,在i从80到0变化的过程中,var%9的变化相当于内层循环,var/9的变话相对于内层循环。这样,作者就妙地用一个变量i同时获得了两个变量的数值。
int counter = 0;for(int i=0 ; i<5 ; i++)    for(int j=0 ; j<4 ; j++)        for(int k=0 ; k<3 ; k++)            printf("counter= %d , i= %d , j=%d , k=%d",counter , i,j,k);            counter++;
可以输出:
counter=0 , i=0, j=0, k=0
counter=1 , i=0, j=0, k=1
counter=2 , i=0, j=0, k=2
counter=3 , i=0, j=1, k=0
counter=4 , i=0, j=1, k=1
....中间略
counter=59 , i=4, j=3, k=2
由输出结果可以看出
k=counter%3; 
j=counter/3 %4;//因为j内有三次循环才轮到j改变一次,而且变化区间固定在本层的[0,4)之间
i=counter/(3*4) %5//i内有3*4次循环才轮到i改变一次,而且变化区间固定在本层的[0,5)之间
于是可以得到
int counter =3*4*5;while(counter--){    printf("cunter= %d , i= %d , j= %d , k= %d" , counter , counter/(3*4)%5 , counter/3%4 , counter%3);}





0 0
原创粉丝点击