文章目录
- 前言
- Floyd 算法
- dijkstra(朴素版)
前言
新手小白记录第一次刷代码随想录
1.自用 抽取精简的解题思路 方便复盘
2.代码尽量多加注释
3.记录踩坑
4.边刷边记录,更有成就感!
5.解题思路绝大部分来自代码随想录
Floyd 算法
【题目描述】
小明喜欢去公园散步,公园内布置了许多的景点,相互之间通过小路连接,小明希望在观看景点的同时,能够节省体力,走最短的路径。
给定一个公园景点图,图中有 N 个景点(编号为 1 到 N),以及 M 条双向道路连接着这些景点。每条道路上行走的距离都是已知的。
小明有 Q 个观景计划,每个计划都有一个起点 start 和一个终点 end,表示他想从景点 start 前往景点 end。由于小明希望节省体力,他想知道每个观景计划中从起点到终点的最短路径长度。 请你帮助小明计算出每个观景计划的最短路径长度。
【输入描述】
第一行包含两个整数 N, M, 分别表示景点的数量和道路的数量。
接下来的 M 行,每行包含三个整数 u, v, w,表示景点 u 和景点 v 之间有一条长度为 w 的双向道路。
接下里的一行包含一个整数 Q,表示观景计划的数量。
接下来的 Q 行,每行包含两个整数 start, end,表示一个观景计划的起点和终点。
【输出描述】
对于每个观景计划,输出一行表示从起点到终点的最短路径长度。如果两个景点之间不存在路径,则输出 -1。
【输入示例】
7 3 1 2 4 2 5 6 3 6 8 2 1 2 2 3
【输出示例】
4 -1
【提示信息】
从 1 到 2 的路径长度为 4,2 到 3 之间并没有道路。
1 <= N, M, Q <= 1000.
思路
Floyd算法核心思想是动态规划。
-
例如我们再求节点1 到 节点9 的最短距离,用二维数组来表示即:grid[1][9],如果最短距离是10 ,那就是 grid[1][9] =10。
-
那 节点1 到 节点9 的最短距离 是不是可以由 节点1 到节点5的最短距离 + 节点5到节点9的最短距离组成呢? 即 grid[1][9] = grid[1][5] + grid[5][9]
-
节点1 到节点5的最短距离 是不是可以有 节点1 到 节点3的最短距离 + 节点3 到 节点5 的最短距离组成呢? 即 grid[1][5] = grid[1][3] + grid[3][5]
-
以此类推,节点1 到 节点3的最短距离 可以由更小的区间组成。那么这样我们是不是就找到了,子问题推导求出整体最优方案的递归关系呢。
-
节点1 到 节点9 的最短距离 可以由 节点1 到节点5的最短距离 + 节点5到节点9的最短距离组成, 也可以有 节点1 到节点7的最短距离 + 节点7 到节点9的最短距离的距离组成。
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt(); // 顶点数int m = sc.nextInt(); // 边数// 初始化距离矩阵,最大值设置为10005final int INF = 10005;int[][] grid = new int[n + 1][n + 1];// 初始化 grid 数组for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (i != j) {grid[i][j] = INF;}}}// 输入边信息for (int i = 0; i < m; i++) {int p1 = sc.nextInt();int p2 = sc.nextInt();int val = sc.nextInt();grid[p1][p2] = val;grid[p2][p1] = val; // 双向图}// Floyd-Warshall 算法//注意k要放在最外层for (int k = 1; k <= n; k++) {for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (grid[i][k] + grid[k][j] < grid[i][j]) {grid[i][j] = grid[i][k] + grid[k][j];}}}}// 输出查询结果int z = sc.nextInt(); // 查询次数while (z-- > 0) {int start = sc.nextInt();int end = sc.nextInt();if (grid[start][end] == INF) {System.out.println(-1);} else {System.out.println(grid[start][end]);}}sc.close(); // 关闭Scanner}
}
dijkstra(朴素版)
【题目描述】
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。
小明的起点是第一个车站,终点是最后一个车站。然而,途中的各个车站之间的道路状况、交通拥堵程度以及可能的自然因素(如天气变化)等不同,这些因素都会影响每条路径的通行时间。
小明希望能选择一条花费时间最少的路线,以确保他能够尽快到达目的地。
【输入描述】
第一行包含两个正整数,第一个正整数 N 表示一共有 N 个公共汽车站,第二个正整数 M 表示有 M 条公路。
接下来为 M 行,每行包括三个整数,S、E 和 V,代表了从 S 车站可以单向直达 E 车站,并且需要花费 V 单位的时间。
【输出描述】
输出一个整数,代表小明从起点到终点所花费的最小时间。
思路
- 第一步,选源点到哪个节点近且该节点未被访问过
- 第二步,该最近节点被标记访问过
- 第三步,更新非访问节点到源点的距离(即更新minDist数组
- minDist数组 用来记录 每一个节点距离源点的最小距离。
- 示例中节点编号是从1开始,所以为了让大家看的不晕,minDist数组下标我也从 1 开始计数,下标0 就不使用了,这样 下标和节点标号就可以对应上了,避免大家搞混
模拟过程
0、初始化
minDist数组数值初始化为int最大值。
这里在强点一下 minDist数组的含义:记录所有节点到源点的最短路径,那么初始化的时候就应该初始为最大值,这样才能在后续出现最短路径的时候及时更新。
源点(节点1) 到自己的距离为0,所以 minDist[1] = 0
此时所有节点都没有被访问过,所以 visited数组都为0
- 模拟过程
以下为dijkstra 三部曲
1.1 第一次模拟
1、选源点到哪个节点近且该节点未被访问过
源点距离源点最近,距离为0,且未被访问。
2、该最近节点被标记访问过
标记源点访问过
3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:
更新 minDist数组,即:源点(节点1) 到 节点2 和 节点3的距离。
源点到节点2的最短距离为1,小于原minDist[2]的数值max,更新minDist[2] = 1
源点到节点3的最短距离为4,小于原minDist[3]的数值max,更新minDist[3] = 4
1.2 第二次模拟
1、选源点到哪个节点近且该节点未被访问过
未访问过的节点中,源点到节点2距离最近,选节点2
2、该最近节点被标记访问过
节点2被标记访问过
3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:
更新 minDist数组,即:源点(节点1) 到 节点6 、 节点3 和 节点4的距离。
以后的过程以此类推
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);// 输入节点数 n 和边数 mint n = sc.nextInt();int m = sc.nextInt();// 定义一个邻接矩阵,初始化为一个很大的数final int INF = Integer.MAX_VALUE;int[][] grid = new int[n + 1][n + 1];// 初始化 grid 为 INF,表示没有直接路径for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (i != j) {grid[i][j] = INF;}}}// 输入边的信息for (int i = 0; i < m; i++) {int p1 = sc.nextInt();int p2 = sc.nextInt();int val = sc.nextInt();grid[p1][p2] = val;}// 设置起点和终点int start = 1;int end = n;// 存储从源点到每个节点的最短距离int[] minDist = new int[n + 1];// 记录顶点是否被访问过boolean[] visited = new boolean[n + 1];// 初始化最短距离数组,起始点到自身的距离为0,其他为INFfor (int i = 1; i <= n; i++) {minDist[i] = INF;}minDist[start] = 0;// 遍历所有节点,执行Dijkstra算法for (int i = 1; i <= n; i++) {int minVal = INF;int cur = -1;// 选择距离起点最近且未访问过的节点for (int v = 1; v <= n; v++) {if (!visited[v] && minDist[v] < minVal) {minVal = minDist[v];cur = v;}}// 如果当前节点无法访问,则跳出循环(即剩下的节点不可达)if (cur == -1) break;visited[cur] = true; // 标记该节点已被访问// 更新非访问节点到源点的最短距离for (int v = 1; v <= n; v++) {if (!visited[v] && grid[cur][v] != INF && minDist[cur] + grid[cur][v] < minDist[v]) {minDist[v] = minDist[cur] + grid[cur][v];}}}// 输出结果,如果终点不可达,输出 -1if (minDist[end] == INF) {System.out.println(-1);} else {System.out.println(minDist[end]);}sc.close(); // 关闭Scanner}
}