Day 53
Floyd 算法
Floyd 算法代码很简单,但真正理解起原理 还是需要花点功夫,大家在看代码的时候,会发现 Floyd 的代码很简单,甚至看一眼就背下来了,但我为了讲清楚原理,本篇还是花了大篇幅来讲解。
题目链接:https://kamacoder.com/problempage.php?pid=1155
文章讲解:https://www.programmercarl.com/kamacoder/0097.小明逛公园.html
视频讲解:https://www.bilibili.com/video/BV15rbzzfEWY
思路分析
floyd 算法的时间复杂度相对较高,适合稠密图且源点较多的情况
(1)Dijkstra 算法处理的是单源最短路(一个起点到多个终点的最短距离)问题,而 Floyd 算法处理的是多源最短路(多个起点到多个终点的最短距离)问题
(2)Floyd 算法对边的权值正负没有要求,都可以处理
(3)Floyd 算法核心思想是动态规划
dp 数组的含义
grid[i][j][k] = m,表示 节点 i 到 节点 j 以[1...k] 集合中的一个节点为中间节点的最短距离为 m
递推公式
(1)经过节点 k:grid[i][j][k] = grid[i][k][k - 1] + grid[k][j][k - 1]
节点 i 到 节点 k 的最短距离 是不经过节点 k,中间节点集合为[1...k-1],所以 表示为 grid[i][k][k - 1]
节点 k 到 节点 j 的最短距离 也是不经过节点 k,中间节点集合为[1...k-1],所以表示为 grid[k][j][k - 1]
(2)不经过节点 k:grid[i][j][k] = grid[i][j][k - 1]
(3)我们是求最短路,对于这两种情况自然是取最小值
dp 数组初始化
从下往上递推,k 那一层的循环需要放在最外面

题解
java
public class FloydBase {
// public static int MAX_VAL = Integer.MAX_VALUE;
public static int MAX_VAL = 10005; // 边的最大距离是10^4(不选用Integer.MAX_VALUE是为了避免相加导致数值溢出)
public static void main(String[] args) {
// 输入控制
Scanner sc = new Scanner(System.in);
System.out.println("1.输入N M");
int n = sc.nextInt();
int m = sc.nextInt();
System.out.println("2.输入M条边");
// ① dp定义(grid[i][j][k] 节点i到节点j 可能经过节点K(k∈[1,n]))的最短路径
int[][][] grid = new int[n + 1][n + 1][n + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 0; k <= n; k++) {
grid[i][j][k] = grid[j][i][k] = MAX_VAL; // 其余设置为最大值
}
}
}
// ② dp 推导:grid[i][j][k] = min{grid[i][k][k-1] + grid[k][j][k-1], grid[i][j][k-1]}
while (m-- > 0) {
int u = sc.nextInt();
int v = sc.nextInt();
int weight = sc.nextInt();
grid[u][v][0] = grid[v][u][0] = weight; // 初始化(处理k=0的情况) ③ dp初始化
}
// ④ dp推导:floyd 推导
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
grid[i][j][k] = Math.min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1]);
}
}
}
System.out.println("3.输入[起点-终点]计划个数");
int x = sc.nextInt();
System.out.println("4.输入每个起点src 终点dst");
while (x-- > 0) {
int src = sc.nextInt();
int dst = sc.nextInt();
// 根据floyd推导结果输出计划路径的最小距离
if (grid[src][dst][n] == MAX_VAL) {
System.out.println("-1");
} else {
System.out.println(grid[src][dst][n]);
}
}
}
}A * 算法 (A star 算法)
一般 笔试或者 面试的时候,不会考察 A*, 都是会结合具体业务场景问 A*算法,例如:地图导航,游戏开发 等等。
其实基础版的 A* 并不难,所以大家不要畏惧,理解本篇内容,甚至独立写出代码,大家可以做到,加油
题目链接:https://kamacoder.com/problempage.php?pid=1203
文章讲解:https://www.programmercarl.com/kamacoder/0126.骑士的攻击astar.html
视频讲解:https://www.bilibili.com/video/BV15RY4zTEdh
思路分析
(1)A * 算法就是改进版的 BFS,通过启发式函数尽可能选取距离源点近的方向进行搜索
使用优先队列实现,对当前节点距离源点的距离进行排序,选取距离源点近的节点出队,同时将其周围方向的节点入队
(2)A * 只擅长给出明确的目标然后找到最短路径
(3)A * 算法的时间复杂度是主要依赖于启发式函数的实现
本题的图是无权网格状,在计算两点距离通常有如下三种计算方式:
(1)曼哈顿距离,计算方式: d = abs(x1-x2)+abs(y1-y2)
(2)欧氏距离(欧拉距离) ,计算方式:d = sqrt( (x1-x2)^2 + (y1-y2)^2 )
(3)切比雪夫距离,计算方式:d = max(abs(x1 - x2), abs(y1 - y2))
x1, x2 为起点坐标,y1, y2 为终点坐标 ,abs 为求绝对值,sqrt 为求开根号,
题解
java
import java.util.*;
class Main {
static int[][] moved = {{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {2, -1}, {2, 1}};
static int endX, endY;
static PriorityQueue<Knight> que = new PriorityQueue<>(new MyCompare());
static int[][] steps = new int[1001][1001];
static class MyCompare implements Comparator<Knight> {
@Override
public int compare(Knight o1, Knight o2) {
return o1.f - o2.f;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while (n-- != 0) {
for (int[] step : steps) Arrays.fill(step, 0);
Knight start = new Knight();
start.x = sc.nextInt();
start.y = sc.nextInt();
endX = sc.nextInt();
endY = sc.nextInt();
start.g = 0;
start.h = distance(start);
start.f = start.g + start.h;
aStar(start);
que.clear();
System.out.println(steps[endX][endY]);
}
}
private static int distance(Knight knight) {
return (knight.x - endX) * (knight.x - endX) + (knight.y - endY) * (knight.y - endY);
}
private static void aStar(Knight knight) {
que.add(knight);
while (!que.isEmpty()) {
Knight cur = que.poll();
if (cur.x == endX && cur.y == endY) return;
for (int[] move : moved) {
int nextX = cur.x + move[0];
int nextY = cur.y + move[1];
if (nextX < 1 || nextX > 1000 || nextY < 1 || nextY > 1000) continue;
if (steps[nextX][nextY] == 0) {
steps[nextX][nextY] = steps[cur.x][cur.y] + 1;
Knight next = new Knight();
next.x = nextX;
next.y = nextY;
next.g = cur.g + 5;
next.h = distance(next);
next.f = next.g + next.h;
que.add(next);
}
}
}
}
}
class Knight {
// 坐标
public int x, y;
// g = 起点到该节点的距离
// h = 该节点到终点的估计距离(因为要按规则,而欧氏距离是直接两点之间)
// f = g + h
public int f, g, h;
}最短路算法总结篇
最各个最短路算法有个全面的了解
文章讲解:https://www.programmercarl.com/kamacoder/最短路问题总结篇.html
