1. 问题描述:
马在中国象棋以日字形规则移动。请编写一段程序,给定 n∗m 大小的棋盘,以及马的初始位置 (x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。
输入格式
第一行为整数 T,表示测试数据组数。每一组测试数据包含一行,为四个整数,分别为棋盘的大小以及初始位置坐标 n,m,x,y。
输出格式
每组测试数据包含一行,为一个整数,表示马能遍历棋盘的途径总数,若无法遍历棋盘上的所有点则输出 0。
数据范围
1 ≤ T ≤ 9,
1 ≤ m,n ≤ 9,
0 ≤ x ≤ n − 1,
0 ≤ y ≤ m − 1
输入样例:
1
5 4 0 0
输出样例:
32
来源:https://www.acwing.com/problem/content/description/1118/
2. 思路分析:
分析题目可以知道这是一道棋盘类的搜索问题,因为需要搜索全部方案,所以需要使用dfs来解决(bfs只能够求解从初始状态到目标状态的最短距离无法求解出所有的方案),每一个棋盘可以看成是一种状态,当前的状态可以往周围的八个方向进行扩展,不同的分支表示不同的方案,所以枚举周围的八个状态可以将所有方案枚举出来,当递归回溯的时候,也即回到上一次调用位置的时候为了保证从调用位置的棋盘状态是一致的所以在回溯的时候需要恢复现场,不过对于python语言来说提交上去超时了,python语言一个非常大的缺点是运行效率比较差,所以使用dfs的时候很容易超时或者爆栈,c++语言会好很多。并且需要注意的一个问题是在递归的时候应该先判断当前访问的位置是否是棋盘中的最后一个位置如果是最后一个位置那么方案数目加1并且直接return即可,如果不是最后一个位置那么标记当前的位置已经被访问,如果先标记然后再判断是否是最后一个格子那么最后一个格子的位置是无法恢复现场的,因为在回溯的时候恢复的是上一次调用的位置所以答案就是错的,方案数目会少很多。
3. 代码如下:
python(超时):
from typing import List
class Solution:
res = 0
def dfs(self, x: int, y: int, n: int, m: int, count: int, st: List[List[int]], pos: List[List[int]]):
# 先判断是否是棋盘中的最后一个格子, 然后标记当前的位置已经被访问
if count == n * m:
# 方案数量加1
self.res += 1
return
st[x][y] = 1
for i in range(8):
a, b = x + pos[i][0], y + pos[i][1]
# 越界了
if a < 0 or a >= n or b < 0 or b >= m: continue
# 之前已经访问过了
if st[a][b] == 1: continue
self.dfs(a, b, n, m, count + 1, st, pos)
# 恢复现场, 虽然说恢复现场的代码写到循环里面或者外面都可以回溯但是写到循环外面运行效果会高一点, 因为当调用完当前位置的所有邻接点的位置之后才恢复现场
st[x][y] = 0
def process(self):
T = int(input())
for i in range(T):
n, m, x, y = map(int, input().split())
self.res = 0
st = [[0] * (m + 10) for i in range(n + 10)]
# 马可以走的八个方向
pos = [[-1, -2], [-2, -1], [-2, 1], [-1, 2], [1, 2], [2, 1], [1, -2], [2, -1]]
self.dfs(x, y, n, m, 1, st, pos)
print(self.res)
if __name__ == '__main__':
Solution().process()
c++:
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10;
int n, m;
bool st[N][N];
int ans;
int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2};
int dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};
void dfs(int x, int y, int cnt)
{
if (cnt == n * m)
{
ans ++ ;
return;
}
st[x][y] = true;
for (int i = 0; i < 8; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= m) continue;
if (st[a][b]) continue;
dfs(a, b, cnt + 1);
}
st[x][y] = false;
}
int main()
{
int T;
scanf("%d", &T);
while (T -- )
{
int x, y;
scanf("%d%d%d%d", &n, &m, &x, &y);
memset(st, 0, sizeof st);
ans = 0;
dfs(x, y, 1);
printf("%d\n", ans);
}
return 0;
}