Description
在一个5 * 6的棋盘中的某个位置有一只马,如果它走29步正好经过除起点外的其他位置各一次,这样一种走法则称马的周游路线,试设计一个算法,从给定的起点出发,找出它的一条周游路线。
为了便于表示一个棋盘,我们按照从上到下,从左到右对棋盘的方格编号,如下所示:
1 2 3 4 5 6
7 8 9 10 11 12
13 14 15 16 17 18
19 20 21 22 23 24
25 26 27 28 29 30
马的走法是“日”字形路线,例如当马在位置15的时候,它可以到达2、4、7、11、19、23、26和28。但是规定马是不能跳出棋盘外的,例如从位置1只能到达9和14。
Input
输入有若干行。每行一个整数N(1<=N<=30),表示马的起点。最后一行用-1表示结束,不用处理。
Output
对输入的每一个起点,求一条周游线路。对应地输出一行,有30个整数,从起点开始按顺序给出马每次经过的棋盘方格的编号。相邻的数字用一个空格分开。
Sample Input
4
-1
Sample Output
注意:如果起点和输入给定的不同,重复多次经过同一方格或者有的方格没有被经过,都会被认为是错误的。
(1)原题中文大意
中国象棋的马按照象棋的规则在5 x 6 的棋盘上跑30步跑完所有的格子。
(2)算法思想及解题用到的主要数据结构
从每一个点往下走第二步,会有几点有效的行走位置。 把当前位置记在栈里面,以深度优先的方式进入下一个有效位置。如果下一个有效位置在之前已经走过,则从栈顶弹出上一位置,恢复到调出的位置后尝试未走过的有效位置。利用函数调用时会压栈的特别,用函数递归调用即可解决问题。
(3)详细解题思路
1. 先初始化一张表。这张表记下了棋盘上所有的30个位置的下一步可走的有效位置.
2. 写一个一般的递归调用自己的函数,表示马在走第几步时到了哪个位置,然后尝试走余下的所有可走位置。
3. 递归函数有两个退出条件。要么下一个要尝试的点已经走过了,要么走到了最后一步,即得解!
(4)逐步求精算法描述
用一个数组记下来经过的点。
(5)程序注释清单
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <vector>
using namespace std;
const static char row_num = 5; // 5行
const static char column_num = 6; // 6列
// 这个全局数组在初始化后会记下每30个有效位置的下一步可走位置.
// elocution[x][0]记个数。最多8个有效可走位置。
char elocution[30][9];
struct coordinate // 坐标.
{
char _x; // start from zero.
char _y;
};
// 坐标转序号的函数。
char coordinate2serial_num(coordinate co)
{
char num = co._x * column_num + co._y + 1;
return num;
}
// 序号转坐标的函数。
coordinate serial_num2coordinate(char sn)
{
coordinate co;
co._x = (sn - 1) / column_num;
co._y = sn - co._x * column_num - 1;
return co;
}
// 增量数组.
char increments[8][2] =
{
1, -2, 2, -1, 2, 1, 1, 2,
-1, 2, -2, 1, -2, -1, -1, -2
};
// 得用增量数组来计算每一个位置的下一步有效位置。记到steps里面.
void next_step(char pos, char steps[])
{
char valid_count = 0;
coordinate co = serial_num2coordinate(pos);
coordinate tmp;
char serial_num;
for ( int i = 0; i < 8; i++ )
{
tmp._x = co._x + increments[i][0];
tmp._y = co._y + increments[i][1];
if ( tmp._x < 0 || tmp._x > 4 || tmp._y < 0 || tmp._y > 5 )
{
continue;
}
serial_num = coordinate2serial_num(tmp);
if ( serial_num >= 1 && serial_num <= 30 )
{
valid_count++;
steps[valid_count] = serial_num;
}
else
{
cerr << "Not expected to reach here.\n";
}
}
steps[0] = valid_count;
}
// 初始化记载所有点的下一步可走点的全局表。
void init_elocution()
{
for ( int i = 1; i <= 30; i++ )
{
next_step(i, elocution[i-1]);
}
}
char track[30]; // 记走过的路径.
bool quit_run_horse;
// 上文提高的递归函用函数.
void run_horse(char start_pos, char step_count)
{
if ( quit_run_horse ) // 用来快速退出深嵌套递归调用函数.
{
return;
}
for ( int i = 0; i < step_count; i++ )
{
// 已?经-经-过y这a点?了?.
if ( track[i] == start_pos )
{
return;
}
}
track[step_count] = start_pos; // 记下新走过的位置
if ( step_count == 29 ) // 最后一步则打印出结果。
{
for ( int i = 0; i < sizeof(track); i++ )
{
cout << (int)track[i];
if ( i + 1 != sizeof(track) )
{
cout << " ";
}
}
cout << endl;
quit_run_horse = true;
return;
}
for ( int i = 0; i < elocution[start_pos-1][0]; i++ )
{
run_horse(elocution[start_pos-1][i+1], step_count+1);
}
}
int main(int argc, char* argv[])
{
init_elocution();
int pos;
vector<int> vec;
while(true)
{
cin >> pos;
if (pos==-1)
{
break;
}
vec.push_back(pos);
}
for ( int i = 0; i < vec.size(); i++ )
{
quit_run_horse = false;
run_horse(vec[i], 0);
}
return 0;
}