"中国象棋将帅问题"的一种解法
在《编程之美:微软技术面试心得》一书中,看到这样一道"中国象棋将帅问题":
下过中国象棋的朋友都知道,双方的"将"和"帅"相隔遥远,并且它们不能照面。在象棋残局中,许多高手能利用这一规则走出精妙的杀招。假设棋盘上中只有"将"和"帅"二子(如图1所示,注:此图仅作示意,原图请见《编程之美:微软技术面试心得》一书)(为了下面叙述方便,我们约定用A表示"将",B表示"帅")。
图 1
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是否互斥。
图 2 用1~9的数字表示A、B的坐标
另一个需要注意的地方是使用一个变量同时存储A、B两个子的位置信息。文中一种解法是一个byte类型的值前后两部分分别表示这两个位置,并给出一系列在byte变量上进行运算的做法,在代码中给出了不少相应的宏定义,代码量相对较多。而第二种解法的代码则很简单,不过其实现思路是怎样的,文中并未直接给出,代码如下:
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);
}
结合文中的这两种解法,个人认为可以使用一个数的个位及十位数分别表示这两个子的位置信息(前提是棋子的位置只是1~9之间,即不超过一个位所能表示的数字范围),这样可以更为直观一些,在时间、空间复杂度上也不见得有所降低。
C++代码如下(在VS 2003中编译通过):
#include <iostream>
#include <windows.h>
using namespace std;
/*
算法:
1.只使用一个变量
2.将双方象棋的位置信息通过十进制数的个位及十位分别表示
3.不在同一列上的双方象棋相对位置为合法位置(通过模运算结果不同实现)
4.去掉个位是0的情况
*/
void main()
{
for(BYTE i = 11;i <= 99;i ++)
{
if(i / 10 % 3 != i % 10 % 3 && ((i % 10) != 0))
{
cout<<"("<<(i / 10)<<","<<(i % 10)<<")/n";
}
}
}
正如书中所言,"问题的本身并不复杂",其实,个人认为这几种解法在本质上是一样的(书中还有另一种解法),这里只是给出一点小想法,如各位还有其他想法或发现文中的不足之处,欢迎分享交流。