小U掌握了国际象棋中“象”和“马”的跳跃能力,在一个无限大的平面直角坐标系中,她每一步可以模仿象和马的跳跃方式移动。在每次询问中,小U需要计算从初始坐标 (x1,y1)
到(x2,y2)
所需的最少步数。
- 象的跳跃:可以移动到
(x+k,y+k)
或(x+k,y−k)
,其中 kk 是任意整数。 - 马的跳跃:可以移动到
(x+a,y+b)
,其中,∣a∣+∣b∣=3
且1≤∣a∣,|b| ≤ 2
。
分析
-
象的跳跃:
- 象的跳跃是沿着对角线移动的,通过对x和y同时加相同的k来实现移动。
-
马的跳跃:
-
马的跳跃是一个特定的模式:可以移动到
(x + a, y + b)
,其中|a| + |b| = 3
且1 ≤ |a|, |b| ≤ 2
。这意味着马的跳跃可以有 8 种可能的移动方式:(x + 1, y + 2)
(x + 1, y - 2)
(x - 1, y + 2)
(x - 1, y - 2)
(x + 2, y + 1)
(x + 2, y - 1)
(x - 2, y + 1)
(x - 2, y - 1)
-
-
求解目标:
- 我们的任务是找到从
(x1, y1)
到(x2, y2)
的最短路径,其中每一步可以模仿象或马的跳跃。 - 由于每一步的移动方式是固定的,我们可以通过广度优先搜索 (BFS) 来找到最短路径。
- 我们的任务是找到从
代码实现
- 定义一个列表来储存马的移动方式:
horse_moves = [
(1, 2), (1, -2), (-1, 2), (-1, -2),
(2, 1), (2, -1), (-2, 1), (-2, -1)
]
2. 象的跳跃方式:
此函数生成象从当前位置 (x, y)
出发,所有可能的跳跃目标。我们使用了一个固定的 k
范围(-50
到 50
)来生成跳跃位置,限制是为了避免无限扩展。如果没有限制,象的跳跃理论上是可以无限远的。可以根据需要调整 k
的范围。
返回值是一个列表,包含了所有可能的跳跃位置。
def generate_bishop_moves(x, y):
moves = []
for k in range(-50, 51):
moves.append((x + k, y + k))
moves.append((x + k, y - k))
return moves
3. 广度优先搜索 (BFS) :
BFS 是一种用于图搜索的算法,适用于寻找最短路径。在这里,我们将坐标平面看作图的节点,跳跃的方式看作边。BFS 会逐层扩展搜索范围,直到找到目标位置为止。
-
边界情况:首先,我们检查如果起点
(x1, y1)
和终点(x2, y2)
是同一个位置,那么就不需要任何跳跃,返回步数0
-
初始化队列:我们使用一个双端队列
queue
来实现 BFS。队列的每个元素是一个三元组(x, y, steps)
,表示当前位置(x, y)
和已经走过的步数steps
。初始化时,起点(x1, y1)
和步数0
被加入队列。 -
访问标记:我们使用一个集合
visited
来记录已经访问过的位置,避免重复搜索。 -
主循环:
- 出队:我们从队列中取出一个位置
(x, y, steps)
,表示当前正在考虑的坐标(x, y)
和已经走的步数steps
。 - 处理马的跳跃:对于每一个马的跳跃方式
(dx, dy)
,我们计算新的位置(nx, ny)
,如果新位置是目标位置(x2, y2)
,我们就返回当前步数steps + 1
。如果新位置没有被访问过,我们将其加入队列并标记为已访问。 - 处理象的跳跃:对于每一个跳跃位置
(nx, ny)
,我们进行相同的检查和更新。
- 出队:我们从队列中取出一个位置
-
BFS 的性质确保了我们找到的第一个到达终点的路径一定是最短路径,因此一旦找到目标点,我们立即返回步数。
完整代码
from collections import deque
# 马的所有可能的跳跃方式
knight_moves = [
(1, 2), (1, -2), (-1, 2), (-1, -2),
(2, 1), (2, -1), (-2, 1), (-2, -1)
]
# 象的跳跃方式:所有满足 x + k, y + k 或 x + k, y - k 的 (k是任意整数)
def generate_bishop_moves(x, y):
moves = []
for k in range(-50, 51): # 选择一个合理的范围,这里假设跳跃不会超过50步
moves.append((x + k, y + k))
moves.append((x + k, y - k))
return moves
def solution(x1, y1, x2, y2):
# 边界情况:起点即是终点
if (x1, y1) == (x2, y2):
return 0
# 初始化 BFS 队列
queue = deque([(x1, y1, 0)]) # (x, y, 步数)
visited = set()
visited.add((x1, y1))
while queue:
x, y, steps = queue.popleft()
# 处理马的跳跃
for dx, dy in knight_moves:
nx, ny = x + dx, y + dy
if (nx, ny) == (x2, y2):
return steps + 1
if (nx, ny) not in visited:
visited.add((nx, ny))
queue.append((nx, ny, steps + 1))
# 处理象的跳跃
for nx, ny in generate_bishop_moves(x, y):
if (nx, ny) == (x2, y2):
return steps + 1
if (nx, ny) not in visited:
visited.add((nx, ny))
queue.append((nx, ny, steps + 1))
# 如果没有找到终点(理论上不会发生)
return -1