Skip to content

Commit 8d4d810

Browse files
Merge pull request #2856 from holic-x/master
docs:补充【0096-城市间货物运输III】JAVA版本的SPFA思路
2 parents 4d36947 + b5cbc15 commit 8d4d810

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed

problems/kamacoder/0096.城市间货物运输III.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,125 @@ public class Main {
702702

703703
```
704704

705+
```java
706+
class Edge {
707+
public int u; // 边的端点1
708+
public int v; // 边的端点2
709+
public int val; // 边的权值
710+
711+
public Edge() {
712+
}
713+
714+
public Edge(int u, int v) {
715+
this.u = u;
716+
this.v = v;
717+
this.val = 0;
718+
}
719+
720+
public Edge(int u, int v, int val) {
721+
this.u = u;
722+
this.v = v;
723+
this.val = val;
724+
}
725+
}
726+
727+
/**
728+
* SPFA算法(版本3):处理含【负权回路】的有向图的最短路径问题
729+
* bellman_ford(版本3) 的队列优化算法版本
730+
* 限定起点、终点、至多途径k个节点
731+
*/
732+
public class SPFAForSSSP {
733+
734+
/**
735+
* SPFA算法
736+
*
737+
* @param n 节点个数[1,n]
738+
* @param graph 邻接表
739+
* @param startIdx 开始节点(源点)
740+
*/
741+
public static int[] spfa(int n, List<List<Edge>> graph, int startIdx, int k) {
742+
// 定义最大范围
743+
int maxVal = Integer.MAX_VALUE;
744+
// minDist[i] 源点到节点i的最短距离
745+
int[] minDist = new int[n + 1]; // 有效节点编号范围:[1,n]
746+
Arrays.fill(minDist, maxVal); // 初始化为maxVal
747+
minDist[startIdx] = 0; // 设置源点到源点的最短路径为0
748+
749+
// 定义queue记录每一次松弛更新的节点
750+
Queue<Integer> queue = new LinkedList<>();
751+
queue.offer(startIdx); // 初始化:源点开始(queue和minDist的更新是同步的)
752+
753+
754+
// SPFA算法核心:只对上一次松弛的时候更新过的节点关联的边进行松弛操作
755+
while (k + 1 > 0 && !queue.isEmpty()) { // 限定松弛 k+1 次
756+
int curSize = queue.size(); // 记录当前队列节点个数(上一次松弛更新的节点个数,用作分层统计)
757+
while (curSize-- > 0) { //分层控制,限定本次松弛只针对上一次松弛更新的节点,不对新增的节点做处理
758+
// 记录当前minDist状态,作为本次松弛的基础
759+
int[] minDist_copy = Arrays.copyOfRange(minDist, 0, minDist.length);
760+
761+
// 取出节点
762+
int cur = queue.poll();
763+
// 获取cur节点关联的边,进行松弛操作
764+
List<Edge> relateEdges = graph.get(cur);
765+
for (Edge edge : relateEdges) {
766+
int u = edge.u; // 与`cur`对照
767+
int v = edge.v;
768+
int weight = edge.val;
769+
if (minDist_copy[u] + weight < minDist[v]) {
770+
minDist[v] = minDist_copy[u] + weight; // 更新
771+
// 队列同步更新(此处有一个针对队列的优化:就是如果已经存在于队列的元素不需要重复添加)
772+
if (!queue.contains(v)) {
773+
queue.offer(v); // 与minDist[i]同步更新,将本次更新的节点加入队列,用做下一个松弛的参考基础
774+
}
775+
}
776+
}
777+
}
778+
// 当次松弛结束,次数-1
779+
k--;
780+
}
781+
782+
// 返回minDist
783+
return minDist;
784+
}
785+
786+
public static void main(String[] args) {
787+
// 输入控制
788+
Scanner sc = new Scanner(System.in);
789+
System.out.println("1.输入N个节点、M条边(u v weight)");
790+
int n = sc.nextInt();
791+
int m = sc.nextInt();
792+
793+
System.out.println("2.输入M条边");
794+
List<List<Edge>> graph = new ArrayList<>(); // 构建邻接表
795+
for (int i = 0; i <= n; i++) {
796+
graph.add(new ArrayList<>());
797+
}
798+
while (m-- > 0) {
799+
int u = sc.nextInt();
800+
int v = sc.nextInt();
801+
int weight = sc.nextInt();
802+
graph.get(u).add(new Edge(u, v, weight));
803+
}
804+
805+
System.out.println("3.输入src dst k(起点、终点、至多途径k个点)");
806+
int src = sc.nextInt();
807+
int dst = sc.nextInt();
808+
int k = sc.nextInt();
809+
810+
// 调用算法
811+
int[] minDist = SPFAForSSSP.spfa(n, graph, src, k);
812+
// 校验起点->终点
813+
if (minDist[dst] == Integer.MAX_VALUE) {
814+
System.out.println("unreachable");
815+
} else {
816+
System.out.println("最短路径:" + minDist[n]);
817+
}
818+
}
819+
}
820+
```
821+
822+
823+
705824
### Python
706825
```python
707826
def main():

problems/kamacoder/0097.小明逛公园.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,71 @@ floyd算法的时间复杂度相对较高,适合 稠密图且源点较多的
424424

425425
### Java
426426

427+
- 基于三维数组的Floyd算法
428+
429+
```java
430+
public class FloydBase {
431+
432+
// public static int MAX_VAL = Integer.MAX_VALUE;
433+
public static int MAX_VAL = 10005; // 边的最大距离是10^4(不选用Integer.MAX_VALUE是为了避免相加导致数值溢出)
434+
435+
public static void main(String[] args) {
436+
// 输入控制
437+
Scanner sc = new Scanner(System.in);
438+
System.out.println("1.输入N M");
439+
int n = sc.nextInt();
440+
int m = sc.nextInt();
441+
442+
System.out.println("2.输入M条边");
443+
444+
// ① dp定义(grid[i][j][k] 节点i到节点j 可能经过节点K(k∈[1,n]))的最短路径
445+
int[][][] grid = new int[n + 1][n + 1][n + 1];
446+
for (int i = 1; i <= n; i++) {
447+
for (int j = 1; j <= n; j++) {
448+
for (int k = 0; k <= n; k++) {
449+
grid[i][j][k] = grid[j][i][k] = MAX_VAL; // 其余设置为最大值
450+
}
451+
}
452+
}
453+
454+
// ② dp 推导:grid[i][j][k] = min{grid[i][k][k-1] + grid[k][j][k-1], grid[i][j][k-1]}
455+
while (m-- > 0) {
456+
int u = sc.nextInt();
457+
int v = sc.nextInt();
458+
int weight = sc.nextInt();
459+
grid[u][v][0] = grid[v][u][0] = weight; // 初始化(处理k=0的情况) ③ dp初始化
460+
}
461+
462+
// ④ dp推导:floyd 推导
463+
for (int k = 1; k <= n; k++) {
464+
for (int i = 1; i <= n; i++) {
465+
for (int j = 1; j <= n; j++) {
466+
grid[i][j][k] = Math.min(grid[i][k][k - 1] + grid[k][j][k - 1], grid[i][j][k - 1]);
467+
}
468+
}
469+
}
470+
471+
System.out.println("3.输入[起点-终点]计划个数");
472+
int x = sc.nextInt();
473+
474+
System.out.println("4.输入每个起点src 终点dst");
475+
476+
while (x-- > 0) {
477+
int src = sc.nextInt();
478+
int dst = sc.nextInt();
479+
// 根据floyd推导结果输出计划路径的最小距离
480+
if (grid[src][dst][n] == MAX_VAL) {
481+
System.out.println("-1");
482+
} else {
483+
System.out.println(grid[src][dst][n]);
484+
}
485+
}
486+
}
487+
}
488+
```
489+
490+
491+
427492
### Python
428493

429494
基于三维数组的Floyd

0 commit comments

Comments
 (0)