POJ 2488 - A Knight's Journey

原题目链接:POJ 2488.
题目大意:
给一个国际象棋棋盘,大小为 p × q p \times q p×q,且 1 ≤ p × q ≤ 26 1 \le p \times q \le 26 1p×q26,每个格子的列数为数字 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. 题目规定了 1 ≤ p × q ≤ 26 1 \le p \times q \le 26 1p×q26 ,理论上代码适用于超出此范围的 p p p q q q ,最大测试到了 9 × \times × 9大小的棋盘可以输出,但运行时间不确定,例如 10 × \times × 5 会比 5 × \times × 10的快很多,原因可能是迭代的过程不同,导致次数差异很大。
  2. 题目要求输出字典序最小的路径,于是考虑只需从A1处开始搜索,并且按照一定的顺序走“日”,如下图所示,能保证搜索得到的路径为最小字典序。


3. 上一点中默认了,如果存在一条符合条件的路径,那么他一定可以从A1处出发;如果从A1出发不存在路径,那么也不存在从其他点出发的路径。这个结论本人无法证明,希望有高手能够解答。(存在路径并不等于存在从任意点出发的路径,这一点是肯定的)

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值