(1.5.1.2)编程之美:中国象棋将帅问题——一个变量实现多重循环

题目:下过中国象棋的朋友都知道,双方的"将"和"帅"相隔遥远,并且它们不能照面。在象棋残局中,许多高手能利用这一规则走出精妙的杀招。假设棋盘上中只有"将"和"帅"二子(如图1所示,注:此图仅作示意,原图请见《编程之美:微软技术面试心得》一书)(为了下面叙述方便,我们约定用A表示"将",B表示"帅")。

中国象棋将帅棋盘表示

A、B二子被限制在已方3×3的格子里运动。例如,在如上的表格里,A被正方形{d10,f10,d8,f8}包围,而B被正方形{d3,f3,d1,f1}包围。每一步,A、B分别可以横向或纵向移动一格,但不能沿对角线移动。另外,A不能面对B,也就是说,A和B不能处于同一纵向直线上(比如A在d10的位置,那么B就不能在d1、d2以及d3的位置上)。

请写出一个程序,输出A、B所有合法位置。要求在代码中只能使用一个变量

文中,作者给出了三种解法,都很巧妙,第一种用到了宏的强大,第二个最为精妙,第三个感觉有点投机,用结构体,那我还能用类啦~!。主要有两点需要注意的地方,一个是如何简单地表示每个格点的位置。在文中,采用了用1~9的数字按照行优先的顺序进行表示(如图2所示),这样,只需要用模余运算就可以得到当前格点列号,从而判断A、B是否互斥。

1-9表示将帅坐标

书中解法一给出的方法是将一个Byte变量拆成两个用,前一半代表“帅”可以走的位置,后一个变量代表“将”可以走的位置(事先已经将“将”和“帅”可以走的3*3的位置进行了编号),利用位操作即可获得两个计数器的功能。书中的解法三采用结构体来解决一个变量遍历二重循环的问题,思想上换汤不换药。真正有趣的是解法二,它的代码如下:

  int var = 81;
  while( var-- )
  {
    if( var / 9 % 3 == var % 9 % 3 )//发生冲突 
     continue;
    else
 printf(/** 打印可行的位置 **/);
  }

  回过头来分析这个解法。“将”和“帅”各在自己的3*3的格子间里面走动,我们共需要验证9*9=81种位置关系,这也是i=81的由来。此外我们要明白i/9和i%9的含义。我们知道,整数i可以由部两分组成,即var=(var/9)*9+var%9 ,其中var<n。我们注意到,在i从81到0变化的过程中,var%9的变化相当于内层循环,var/9的变话相对于内层循环。这样,作者就妙地用一个变量i同时获得了两个变量的数值。

可以把变量i想象成一个两位九进制的变量,而i在计算机中存储的值是i的十进制表示。则i/9的计算机处理结果,即结果直接去掉小数点后部分的结果即是此九进制数的第二位,而i%9即是此九进制数的个位。本程序用此九进制数的第二位保存A的位置,个位表示B的位置。最大值为88,即为十进制的80.程序从十进制的80,即九进制的88遍历到十进制的0,即九进制的0。将符合条件的位置全部输出。

问题扩展:如何利用一个变量达到三重循环的效果。也就是说,如果给定下面的循环:

 int counter = 0;

 for( int i = 0; i < 5; i++ )  

for( int j = 0; j < 4; j++ )   

for( int k = 0; k < 3; k++ )   

{    

System.out.println("counter="+counter+"\t, i="+i+", j="+j+", k="+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

 问题是

(1)我们如何用一个打印出相同的结果?

(2)如果是N重循环呢?   

面对第一个问题,实际上就是对原始的中国象棋将帅问题进行了一个扩展,即在棋盘上添加一个“王”,其行走规则和将帅 一样。于是棋盘变成了三国争霸:-) ,将帅王可以走动的格子数分别为3、4、5,它们之间的互斥条件可以按需要设定。        

这时,就需要只用一个变量遍历一个三重循环。直观的方法是像方法一那样把一个4字节的INT拆开来用。

我这里只关注方法二。        

只用一个变量解决扩展的中国象棋将帅问题,我们的代码应该是如下的样子:   

int var = 3*4*5;   

while( var-- )   

{     

if( /** 冲突条件 **/ )//发生冲突      

   continue;     

else            

printf(/** 打印可行的位置 **/);    }

在冲突条件中,我们需要知道var取得某个特定的值(即第var+1次循环)的时候的i,j,k分别是多少(这样我们才能判定将帅位置是否冲突)     

从上例的结果中我们可以看到,counter的值(即当前的循环次数)和三元组(i,j,k)是一一对应的,越是外层的循环变化越慢,他们满足什么关系呢?     

 k的取值最好确定,我们都知道是var%3。     

在原始的将帅问题中我们知道,j的值应该是 var/3,但是由于j上面还有一层循环,就需要做些调整,变成var/3%4     

最外层循环i的值则为(var/(3*4))%5.           即:

k=var%3      //其下没有循环了             

 j=var/3   //其下有几个循环长度为3的循环            

 i=var/(3*4). //其下有几个循环长度为3*4的循环      

于是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 < 3; p++ )       

 p=var%2 //其下没有循环了   

k=var/2      //其下有几个循环长度为2的循环   

j=var/(2*3)) //其下有几个循环长度为2*3的循环   

i=var/(2*3*4)//其下有几个循环长度2*3*4的循环      

下面就是一个变量实现三重循环

int var = 2*3*4*5;

while( var-- > 0)

{  

System.out.println("var="+var+" , i="+((var/(2*3*4))%5)+

                              ", j ="+((var/(2*3))%4)+",  

                               k="+((var/2)%3)+",                                                       

                               p="+var%2);

}

结果是:   

var=119 , i=4, j=3, k=2, p=1   

var=118 , i=4, j=3, k=2, p=0   

var=117 , i=4, j=3, k=1, p=1   

...中间略   

var=5 , i=0, j=0, k=2, p=1   

var=4 , i=0, j=0, k=2, p=0   

var=3 , i=0, j=0, k=1, p=1   

var=2 , i=0, j=0, k=1, p=0   

var=1 , i=0, j=0, k=0, p=1   

var=0 , i=0, j=0, k=0, p=0        

 N重循环原理也是一样,就不再赘述了。



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值