问题描述
小U掌握了国际象棋中“象”和“马”的跳跃能力,在一个无限大的平面直角坐标系中,她每一步可以模仿象和马的跳跃方式移动。在每次询问中,小U需要计算从初始坐标 (x1,y1) 到 (x2,y2) 所需的最少步数。
- 象的跳跃:可以移动到 (x+k,y+k) 或 (x+k,y−k),其中 k 是任意整数。
- 马的跳跃:可以移动到 (x+a,y+b),其中 ∣a∣+∣b∣=3 且 1≤∣a∣,∣b∣≤2。
你需要在每次询问中计算从起点到终点所需的最少步数。
思路分析
-
简化问题:
- 将起点设为原点 (0,0),终点设为 (dx,dy),其中 dx=x2−x1和 dy=y2−y1。
-
使用广度优先搜索 (BFS) :
-
BFS 可以有效地找到最短路径,因为它逐层扩展节点,确保最先到达目标的路径是最短的。
-
维护一个队列来进行 BFS,并使用一个集合来记录已经访问过的状态,避免重复计算。
-
-
跳跃规则:
- 象的跳跃规则可以表示为 (±k,±k)(±k,±k)。
- 马的跳跃规则可以表示为所有满足 ∣a∣+∣b∣=3且 1≤∣a∣,∣b∣≤2 的组合。
-
终止条件:
- 当我们达到目标位置 (dx,dy)(dx,dy) 时,返回当前步数。
举例
比如说,假设我们要从 (0,0) 移动到 (2,1):
-
初始状态:
- 队列:[(0,0,0)] (当前位置为 (0,0),步数为 0)
- 已访问:{(0,0)}
-
第一次迭代:
-
弹出 (0,0,0)
-
尝试所有骑士跳跃:
- (2,1) -> 步数变为 1
-
达到目标位置 (2,1)(2,1),返回步数 1
-
代码详解
以下是 Java 实现代码:
import java.util.*;
public class Main {
public static int solution(int x1, int y1, int x2, int y2) {
int dx = x2 - x1;
int dy = y2 - y1;
int[][] knightMoves = {
{2, 1}, {2, -1}, {-2, 1}, {-2, -1},
{1, 2}, {1, -2}, {-1, 2}, {-1, -2}
};
List<int[]> bishopMoves = new ArrayList<>();
for (int k = 1; k <= Math.max(Math.abs(dx), Math.abs(dy)); k++) {
bishopMoves.add(new int[]{k, k});
bishopMoves.add(new int[]{-k, k});
bishopMoves.add(new int[]{k, -k});
bishopMoves.add(new int[]{-k, -k});
}
Queue<int[]> queue = new LinkedList<>();
Set<String> visited = new HashSet<>();
queue.offer(new int[]{0, 0, 0});
visited.add("0,0");
while (!queue.isEmpty()) {
int[] current = queue.poll();
int cx = current[0];
int cy = current[1];
int steps = current[2];
if (cx == dx && cy == dy) {
return steps;
}
for (int[] move : knightMoves) {
int nx = cx + move[0];
int ny = cy + move[1];
String key = nx + "," + ny;
if (!visited.contains(key)) {
visited.add(key);
queue.offer(new int[]{nx, ny, steps + 1});
}
}
for (int[] move : bishopMoves) {
int nx = cx + move[0];
int ny = cy + move[1];
String key = nx + "," + ny;
if (!visited.contains(key)) {
visited.add(key);
queue.offer(new int[]{nx, ny, steps + 1});
}
}
}
return -1;
}
public static void main(String[] args) {
System.out.println(solution(0, 0, 1, 1) == 1);
System.out.println(solution(0, 0, 2, 1) == 1);
System.out.println(solution(0, 0, 3, 3) == 1);
System.out.println(solution(-3, -2, 2, 1) == 2);
}
}
思考总结
通过解决这道题目,我学到了以下几点:
-
广度优先搜索 (BFS) :
- BFS 是一种用于遍历或搜索树或图的算法。它按层次遍历,适合寻找无权图的最短路径。
-
数据结构的选择:
- 使用队列 (
Queue) 来存储待处理的状态。 - 使用集合 (
Set) 来记录已访问的状态,避免重复计算。
- 使用队列 (
-
复杂度分析:
- 时间复杂度主要取决于状态空间的大小和每个状态的转移次数。在本题中,由于跳跃规则有限,时间复杂度是可以接受的。
学习建议
- 对于初学者来说,掌握基本的数据结构和算法是非常重要的。
- 多做题多思考,尝试不同的解法,不断优化自己的代码。
- 学会阅读和理解别人的代码,从中吸取经验。
一个月的时间转瞬即逝,以下是我使用AI刷题的个人看法:
随着AI大模型的飞速发展,AI在编程领域的应用越来越广泛。它不仅改变了软件开发的方式,还带来了诸多利弊。AI可以自动化重复性任务,比如一些基础代码的生成、调试和测试,大幅减少开发者的工作量。AI可以帮助我们更快地编写代码。就我本身而言,我比较喜欢询问代码的解释,像之前AI模型没有流行的时候,我只能在课堂上听老师对代码的讲解,有的时候听不懂又不好意思询问老师,但现在有了这些工具,真的方便了很多。
AI可以通过静态分析检测潜在的错误和漏洞,提高代码质量。机器学习模型可以自动优化代码性能,使其更加高效。新手可以通过AI工具快速了解复杂的概念和实现方法。并且可以通过在线教程和文档推荐系统根据个人进度提供定制化学习资源。在豆包MarsCode中,我比较喜欢代码提示和检查代码的功能,因为只有几年的码代码经验以及我个人的代码思维比较差,这些功能对于我来说非常适合。
除此之外,过度依赖AI可能导致我们失去基础技能,影响其独立解决问题的能力。事物没有完美的,AI工具也不例外,其出现问题或无法处理特定情况时,可能会导致工作停滞。而且使用AI工具时,代码和数据可能需要上传到云端,存在泄露敏感信息的风险。开源AI模型也可能存在安全漏洞,会被恶意利用。
随着AI的发展,某些初级编程岗位可能会被自动化取代,增加失业风险。所以需要我们不断提升自身技能,以适应不断变化的技术环境。