中国象棋将帅问题

来源:互联网 发布:蜂窝移动网络下载 编辑:程序博客网 时间:2024/04/27 09:27
将帅问题
俗话说的好,将帅不见面,见面分胜负。残局之中只有将帅,如何保持僵局?假设A表示将,B表示帅。如何输出他们的合法位置?请写一个程序来输出。


当你看到这个题目的时候,可能感觉很简单,无非是两个个for循环而已,只要A、B不在同一列即可。可是你如何表示表示棋盘,如何表示棋局呢?很麻烦?是吧哈哈,其实这根本不是重点,棋局什么的都不重要,重要的是A、B的合法位置。这个时候就要学会转化一下,数学建模,把这棋局转化为逻辑坐标。也就是说,我们只要一个3×3的数组就可以表示他们的所有位置了。要看清本质问题啊,不要想那些没用的。

类似的 A=4,B=8就是一个合法位置,ok,let’s programming

int main(){for(int i=1;i<=9;i++){for(int j=1;j<=9;j++){if(i%3!=j%3)cout<<"A="<<i<<"  B="<<j<<endl;}}return 0;}

呵呵,还不错。可是如果我说只能一个变量呢?你能想到什么?一个变量,用变量的一半当作i,另一半用来当作j。这时候你可能会想到位结构体,这个位结构体不赞成使用,因为它降低程序的可移植性。不过,有时候会起到很巧妙的效果,就像这个题目。
struct A{unsigned int a:4;unsigned int b:4;};int main(){A 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) cout<<"A="<<i.a<<"  B="<<i.b<<endl;}}return 0;}

几乎和刚才的代码没什么区别。不错。还有别的好的办法吗?你会用到位操作吗?一个char类型8位 ,4位足够表示9个数,所以用一个char的左边4位和右边4位来代替i,j是完全可以的。可是很麻烦的,要进行或、与操作,分别取一个char型的左边4位和右边4位。可是我们要掌握这种方法,位操作通常也会解决一些很麻烦的问题,常常有巧妙的解法。如果我们利用一个8位的char变量,需要提取他的左边4位和右边四4位的值。这只需要简单的相与相或加移位就可以办到了。获取a的前四位,只需要((a&240)>>4)  a=10100101举例         10100101 (a)& 11110000 (240)-----------------   10100000>>4=00001010

获取a的前四位,只需要(a&15) a=10100101

举例     10100101 (a)      & 00001111 (15)--------------00000101可是我们如何给循环变量加增量呢?同样利用位操作。给右边四位赋值(重置),同时还要保持左边四位的值。因为这要代替两个变量,不能相互影响,和获取不同的是改变了的值要保存。a的右边四位重置为n,同时保持左边四位不变。(a=(240&a)|n)举例 a=10100101  n=1   11110000 (240)& 10100101 (a)-----------------  10100000 | 00000001 (n)-----------------  10100001

a的左边四位重置为n,同时保持右边四位不变。(a=(15&a)|(n<<4))举例  a=10100101   n=1  00001111(15)&10100101(a)---------------- 00000101 | 00010000 (n<<4)---------------  00010101搞定。可是我们如何利用循环呢,我们如何让变量递增,写一个函数吗?循环表达式的条件还是有的。这个时候我们就可以利用define的特性,进行文本替换。#define RightReset(a,n) (a=(240&a)|n) //重置a的右边四位为n,同时保持a的左边四位。240=二进制(11110000#define LeftReset(a,n)  (a=(15&a)|(n<<4))//重置a的左边四位为n,同时保持a的右边边四位。15=二进制(00001111#define GetRight(a)    (a&15)    //获取a的右边四位的值#define GetLeft(a)    ((a&240)>>4) //获取a的左边四位的值
#include<iostream>using namespace std;//像这种情况只可以用define来处理了。。#define RightReset(a,n) (a=(240&a)|n) //重置a的右边四位为n,同时保持a的左边四位。240=二进制(11110000)#define LeftReset(a,n)  (a=(15&a)|(n<<4))//重置a的左边四位为n,同时保持a的右边边四位。15=二进制(00001111)#define GetRight(a)    (a&15)    //获取a的右边四位的值#define GetLeft(a)    ((a&240)>>4) //获取a的左边四位的值int main(){unsigned char a=0;for(RightReset(a,1);GetRight(a)<=9;RightReset(a,(GetRight(a)+1)))//利用define可以使一个变量足以解决{for(LeftReset(a,1);GetLeft(a)<=9;LeftReset(a,(GetLeft(a)+1)))if(GetRight(a)%3!=GetLeft(a)%3)cout<<"A="<<GetRight(a)<<"  B="<<GetLeft(a)<<endl;}return 0;}
我想问一句,这个非要两个for循环吗,看似不可避免,其实不然。有一种方法可以代替for循环的效果。就是利用除运算符和求模运算符。比如i/9和i%9,当i=80时,i/9=8,i%9=8;当i=79时,i/9=8,i%9=7;当i=78时,i/9=8,i%9=6;所以带来的效果就像是for循环,只需要在写代码的时候稍作修改即可。81=9×9;

int main(){for(int i=80;i>0;i--){if((i/9)%3!=(i%9)%3)cout<<"A="<<i/9+1<<"  B="<<i%9+1<<endl;}return 0;}*/

这种利用一个变量解决循环的问题可以扩展成多重循环,只要写好判别的条件就好了,就ok。如何利用一个变量解决4重循环呢,当然位操作可以。可是我们要用刚才说的方法。比如解决  for( int i = 0; i < 5; i++ )     for( int j = 0; j < 4; j++ )       for( int k = 0; k < 3; k++ )         for( int p = 0; p < 2; p++ )这样的循环我们定义变量var,此时var=2*3*4*5,注意循环的过程var--。越是外层的循环变化越慢,他们满足什么关系呢?i只需要循环5次就好了,j要循环4*5次,k要循环3*4*5次,p要循环2*3*4*5次。 故循环次数 i=var/(2*3*4) j=var/(2*3)) k=var/2   p=var所以i的循环要利用(var/(2*3*4))%5,j的循环要利用(var/(2*3))%4,k的循环要利用(var/(2))%3 ,p的循环要利用(var)%2。就像刚才的象棋将帅问题,var=81, i=var/9,j=var。所以循环变量就是(var/9)%9和var%9。代码完全可以改为

int main(){for(int i=80;i>0;i--){if(((i/9)%9)%3!=(i%9)%3)cout<<"A="<<i/9+1<<"  B="<<i%9+1<<endl;}return 0;}

所以掌握方法真的很重要。
转载请注明出处http://blog.csdn.net/sustliangbo/article/details/9297187


原创粉丝点击