原题目链接:POJ 2488.
题目大意:
给一个国际象棋棋盘,大小为
p
×
q
p \times q
p×q,且
1
≤
p
×
q
≤
26
1 \le p \times q \le 26
1≤p×q≤26,每个格子的列数为数字
p
p
p,行数为第
q
q
q个字母,例如A1、C4、H8等。求从任意一点出发,按照国际象棋中“马”的走法,是否能将棋盘的所有位置走一遍(不能重复)。第一行输入样例数
n
n
n,下面
n
n
n行输入
n
n
n组"
p
q
p \ q
p q",若存在路径,输出字典序最小的路径;若不存在,输出“impossible”。
样例:
input:
3
1 1
2 3
4 3
output:
Scenario #1:
A1
Scenario #2:
impossible
Scenario #3:
A1B3C1A2B4C2A3B1C3A4B2C4
思路:
用DFS+回溯,当“马”下一步的位置在棋盘内,且没被走过,走出这一步,将此位置标记为已走;如果下一步无路可走,回溯。跳出条件为全部位置都走过。
代码(C):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_M 27
#define MAX_N 27
char flag[MAX_M][MAX_N];
struct step {
int x;
char y;
} path[MAX_M*MAX_M];
char success;
int cases, p, q;
const int dx[8] = { -1, 1, -2, 2, -2, 2, -1, 1 };
const int dy[8] = { -2, -2, -1, -1, 1, 1, 2, 2 };
void DFS(int x, int y, int num) {
path[num].y = y + 'A' - 1;
path[num].x = x;
if (num == p * q) {
success = 1;
return;
}
for (int i = 0; i < 8; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (0 < nx && nx <= p && 0 < ny && ny <= q && flag[nx][ny] != 1 && success != 1) {
flag[nx][ny] = 1;
DFS(nx, ny, num + 1);
flag[nx][ny] = 0;
}
}
}
int main() {
scanf("%d",&cases);
for (int i = 1; i <= cases; i++) {
scanf("%d %d", &p, &q);
success = 0;
memset(flag, 0, sizeof(flag));
printf("Scenario #%d:\n", i);
flag[1][1] = 1;
DFS(1, 1, 1);
if (success) {
for (int i = 1; i <= p * q; i++) {
printf("%c%d", path[i].y, path[i].x);
}
printf("\n");
} else {
printf("impossible\n");
}
if (i != cases) {
printf("\n");
}
}
return 0;
}
几点思考:
- 题目规定了 1 ≤ p × q ≤ 26 1 \le p \times q \le 26 1≤p×q≤26 ,理论上代码适用于超出此范围的 p p p 和 q q q ,最大测试到了 9 × \times × 9大小的棋盘可以输出,但运行时间不确定,例如 10 × \times × 5 会比 5 × \times × 10的快很多,原因可能是迭代的过程不同,导致次数差异很大。
- 题目要求输出字典序最小的路径,于是考虑只需从A1处开始搜索,并且按照一定的顺序走“日”,如下图所示,能保证搜索得到的路径为最小字典序。
3. 上一点中默认了,如果存在一条符合条件的路径,那么他一定可以从A1处出发;如果从A1出发不存在路径,那么也不存在从其他点出发的路径。这个结论本人无法证明,希望有高手能够解答。(存在路径并不等于存在从任意点出发的路径,这一点是肯定的)