问题描述
小U掌握了国际象棋中“象”和“马”的跳跃能力,在一个无限大的平面直角坐标系中,她每一步可以模仿象和马的跳跃方式移动。在每次询问中,小U需要计算从初始坐标 (x1,y1)(x1,y1) 到 (x2,y2)(x2,y2) 所需的最少步数。
- 象的跳跃:可以移动到 (x+k,y+k)(x+k,y+k) 或 (x+k,y−k)(x+k,y−k),其中 kk 是任意整数。
- 马的跳跃:可以移动到 (x+a,y+b)(x+a,y+b),其中 ∣a∣+∣b∣=3∣a∣+∣b∣=3 且 1≤∣a∣,∣b∣≤21≤∣a∣,∣b∣≤2。
你需要在每次询问中计算从起点到终点所需的最少步数。
测试样例
样例1:
输入:
x1 = 0, y1 = 0, x2 = 1, y2 = 1
输出:1
样例2:
输入:
x1 = 0, y1 = 0, x2 = 2, y2 = 1
输出:1
样例3:
输入:
x1 = 0, y1 = 0, x2 = 3, y2 = 3
输出:1
样例4:
输入:
x1 = -3, y1 = -2, x2 = 2, y2 = 1
输出:2
以下是对这道题目的分析: ### 一、题目理解
- 移动规则明确:题目详细说明了“象”和“马”在无限大平面直角坐标系中的移动规则。象可以沿着特定对角线方向以任意整数倍的步长移动,马则按照特定的坐标增量组合移动,且每次移动的坐标增量绝对值之和为3,同时每个坐标增量的绝对值在1到2之间。
- 目标清晰:给定起点坐标和终点坐标,需要计算从起点到达终点所需的最少步数。这是一个典型的路径搜索问题,只不过移动方式有特定的规则限制。
二、解题思路分析
- 广度优先搜索(BFS)的适用性:由于要找从起点到终点的最少步数,广度优先搜索是一个比较合适的算法选择。BFS可以按照距离起点由近及远的顺序逐层搜索,一旦找到终点,此时的层数就是最少步数。
- 状态表示与扩展: - 状态可以用坐标来表示当前所在的位置。 - 对于状态扩展,需要根据象和马的移动规则分别生成新的可达状态。例如对于象,从当前位置可以生成和(为任意整数)这两种新状态;对于马,从当前位置可以根据其移动规则生成相应的几种新状态。
- 避免重复搜索:为了提高搜索效率,需要记录已经访问过的状态,避免重复搜索同一个位置,否则可能会陷入无限循环或者导致搜索效率极低。可以使用一个集合或者哈希表来记录已经访问过的坐标。 ### 三、测试样例分析
- 样例1:输入
x1 = 0, y1 = 0, x2 = 1, y2 = 1,输出1。从起点到终点,象可以通过移动到一步到达,所以最少步数为1。 - 样例2:输入
x1 = 0, y1 = 0, x2 = 2, y2 = 1,输出1。马可以从移动到一步到达终点,所以最少步数为1。 - 样例3:输入
x1 = 0, y1 = 0, x2 = 3, y2 = 3,输出1。象可以通过移动到一步到达终点,所以最少步数为1。 - 样例4:输入
x1 = -3, y1 = -2, x2 = 2, y2 = 1,输出2。可能的一种移动路径是先通过马的移动从到某个中间位置,再通过象或马的移动到达终点,经过分析计算最少需要2步。
四、总结感悟
- 问题转化的重要性:本题将国际象棋中棋子的移动规则转化为在平面直角坐标系中的路径搜索问题,这让我体会到将实际问题或特定情境下的问题转化为常见的算法模型(如本题中的路径搜索问题,可通过BFS解决)是解决问题的关键一步。
-
- 多种移动规则的处理:面对象和马两种不同的移动规则,需要分别准确地实现其状态扩展,这锻炼了我对复杂规则的理解和代码实现能力。在处理类似有多种不同规则的问题时,要仔细分析每种规则的特点并准确地转化为代码逻辑。
-
- 搜索算法的应用:广度优先搜索算法在本题中很好地发挥了作用,通过逐层搜索找到最少步数。这再次证明了掌握常见搜索算法及其应用场景的重要性,在遇到类似求最少步数、最短路径等问题时,可以迅速联想到合适的搜索算法来解决问题。
-
- 效率优化:在代码实现中,通过记录已访问过的状态来避免重复搜索,这是提高搜索效率的重要手段。在解决其他搜索相关问题时,也应注意采取类似的措施来避免不必要的计算和循环,提高算法的整体效率。
- 代码实现: from collections import deque
def solution(x1, y1, x2, y2): # 象的跳跃方向 bishop_directions = [(1, 1), (1, -1), (-1, 1), (-1, -1)] # 马的跳跃方向 knight_directions = [(2, 1), (2, -1), (-2, 1), (-2, -1), (1, 2), (1, -2), (-1, 2), (-1, -2)]
visited = set()
queue = deque([(x1, y1, 0)])
visited.add((x1, y1))
while queue:
x, y, steps = queue.popleft()
if x == x2 and y == y2:
return steps
# 象的跳跃
for dx, dy in bishop_directions:
k = 1
while True:
new_x, new_y = x + k * dx, y + k * dy
if (new_x, new_y) not in visited:
visited.add((new_x, new_y))
queue.append((new_x, new_y, steps + 1))
k += 1
if k > abs(x2 - x1) + abs(y2 - y1):
break
# 马的跳跃
for dx, dy in knight_directions:
new_x, new_y = x + dx, y + dy
if (new_x, new_y) not in visited:
visited.add((new_x, new_y))
queue.append((new_x, new_y, steps + 1))
return -1
测试用例
print(solution(0, 0, 1, 1)) # 输出: 1 print(solution(0, 0, 2, 1)) # 输出: 1 print(solution(0, 0, 3, 3)) # 输出: 1 print(solution(-3, -2, 2, 1)) # 输出: 2