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

//象棋问题的解法
//使用val变量左四位总共16个可取值代表A可能的位置(只需取0-8)
//使用val变量右四位总共16个可取值代表B可能的位置(只需取0-8)
#define GRDW 3
int 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-0
while(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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值