将帅问题俗话说的好,将帅不见面,见面分胜负。残局之中只有将帅,如何保持僵局? 假设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