今天认真把中国象棋将帅问题看了一遍,确实如其它同学所说—不难,只要自己理解了其中的思想,其他的只是实现方法不一样而已。下面,我也捋一捋自己的想法。
其实分析到用九个数字表示格点位置是关键,这样只需要用取余运算就可判断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循环来遍历,但需要改进。