藍橋杯圖論 Dijkstra 單源最短路講解
什麼是單源最短路?
說實話,兄弟剛接觸這個東西的時候是懵逼的,單源最短路?哇,難不成是以前看的科幻電影中的高科技武器的科技?
學習過程中也很懵逼,給很多文字誤導了其實就一句話:“從一個源頭出發,到任意一個節點的最短距離”
Dijkstra算法是怎麼被想出來的?
看見書上稀稀疏疏的寫了一大堆,反正就是沒看懂。邏輯歸納出來就是,爲了證明而證明,並不符合人類的正常認識。
正常人的認識肯定是,從當前點出發,優先走最短的路徑,則到達的點的路徑,肯定是最短的。
簡單的舉一個例子:如果你從一棟樓出發,有任意條道路,可以到另外的一些樓。那麼,你肯定會用腦子想。
我現在走最短的路徑,走到下一棟樓,那麼我到下一棟樓所走的路肯定是最近的。
BUT,如果下一棟樓的距離加下一棟樓到另外一棟樓的距離出發(或以前任意樓出發的距離)
超過了從第一棟樓出發的距離,那麼這條路不是最短。
理論證明
路徑:從源頭出發加上,一條由所經過邊消耗組成的節點集合。
最短節點路徑:由初始邊到達目標節點最短的節點集合。
1、可知,由源頭節點A出發,有任意一節點C,可以用A->B->C或A->C訪問。
這個時候我們比較那條路更短,實際上比較的是(A-B)->C與A->C路徑大小
歸納的看來,可以認爲,我們實質上是在比較要到達任意一個節點C的最短路徑,那麼就是他前面所走路徑加上從該路徑出發到C的距離。
2、從A出發,到達B的距離既爲最小,因爲到達B的消耗是A最小的邊。
不可能存在一邊D可使得A->D->B爲最短。
則若 A->B->C 小於A->C 則不可能存在第四點小於A->B->C
因此,可以理解爲從=目前已探索到的最短路徑到達的節點出發,如果可以聯通到點C。並且小於A->C距離,則A-B-C
爲最短路徑(貪心算法,總體路徑最短)
思路實現(堆優化)
現在有N路徑,都可以到達C,那我們是不是把這兩條路徑按照升序排列。
第0位上的爲最小,它的路徑長度就是最短路徑大小。
如:(C1=1,C2=3,C3=4)
C1 C2 C3的路徑是由什麼轉變過來的呢?答:上一步的路徑+達到C的花費
看到這裏大概會想到,優先隊列來存放到達C的路徑,不就是我們理論證明的思路嗎?
優先隊列只要訪問到C,那麼C的路徑就是最小
import java.util.*;
/**
有向圖,dijkstra
**/
public class 單源最短路徑重置 {
static int V;
static int E;
static int target;
static int count = 0;
static Edge[] edges;
static int[] head;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
V = sc.nextInt();
E = sc.nextInt();
target = sc.nextInt();
edges = new Edge[E];
head = new int[V + 1];
for (int i = 0; i <= V; i += 1)
head[i] = -1;
for (int i = 0; i < E; i++) {
addEdge(sc.nextInt(), sc.nextInt(), sc.nextInt());
}
dijkstra();
}
public static void dijkstra() {
boolean[] vis = new boolean[V+1];
int[] disTo = new int[V + 1];
PriorityQueue<node> pq = new PriorityQueue<node>();
pq.add(new node(target, 0));
for(int i=0;i<disTo.length;i++)disTo[i]=Integer.MAX_VALUE;
disTo[target]=0;
while (!pq.isEmpty()) {
node tmp = pq.poll();
if (vis[tmp.to])
continue;
vis[tmp.to] = true;
disTo[tmp.to] = tmp.weight;
for (int i = head[tmp.to]; i != -1; i = edges[i].next) {
Edge nextEdge=edges[i];
if(tmp.weight+nextEdge.weight<disTo[nextEdge.to]) {
disTo[nextEdge.to]=tmp.weight+nextEdge.weight;
pq.offer(new node(nextEdge.to,disTo[nextEdge.to]));
}
}
}
for(int i =1;i<disTo.length;i++)System.out.print(disTo[i]+" ");
}
public static class node implements Comparable<node>{
int to, weight;
public node(int to, int weight) {
this.to = to;
this.weight = weight;
}
@Override
public int compareTo(node o) {
return this.weight-o.weight;
}
}
public static void addEdge(int from, int to, int weight) {
edges[count] = new Edge(to, weight, head[from]);
head[from] = count++;
}
public static class Edge {
int to, weight, next;
public Edge(int to, int weight, int next) {
this.to = to;
this.weight = weight;
this.next = next;
}
}
}