Shortest path
1 简介
给定有向带权图G=(V,E),E中的每条边权值为非负实数。给定一点s,求s到其它点的最短路径。
对于这类问题,我们常用Dijkstra算法。
所谓Dijkstra算法,即开始时,集合S为空,集合Q为所有点以及它们到s的距离。之后,如果Q不为空,那选取Q中与s距离最短的点u,将该点加入S,之后更新u的所有临近点v,如果 d s v > d s u + d u v d_{sv}>d_{su}+d_{uv} dsv>dsu+duv,那么 d s v = d s u + d u v d_{sv}=d_{su}+d_{uv} dsv=dsu+duv。
伪代码如下:
S={}
Q=G.V
while Q!={}:
u=Extract_min(Q)
S=SU{u}
for each vertex v connect with u:
Update(v)
2 证明
在 算 法 执 行 过 程 中 , ∀ u ∈ S , 路 径 P s u 是 最 短 的 。 在算法执行过程中,\forall u\in S, 路径P_{su}是最短的。 在算法执行过程中,∀u∈S,路径Psu是最短的。
证明(用演绎法):
|S|=1,S={s},Pathss=0。
假设|S|=k(k ≥ 1)时该说法仍然成立。
现在将v加入S,使得|S|=k+1。令(u,v)为路径 P s v P_{sv} Psv上的最后一条边。
根据假设, P s u P_{su} Psu是s到u的最短路径。假设存在另外一条s-v的路径P,我们需要证明 P ≥ P s v P ≥ P_{sv} P≥Psv 。为了到达v,该路径必然在某处离开集合S,设y为该路径离开S后经过的第一个节点,x为y之前的点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kjX7SC8s-1594293018073)(D:\CS-Recorder\CS-2-2\ADA\dijkstra-proof.png)]
根据算法,每次会选择S之外离s最近的点,因此有如下不等式成立:
l ( P s x + ( x , y ) ) ≥ l ( P s u + ( u , v ) ) , 所 以 l ( P ) = l ( P s x + ( x , y ) + P y v ) ≥ l ( P s u + ( u , v ) ) = l ( P s v ) l(P_{sx}+(x,y))\ge l(P_{su}+(u,v)),所以l(P)=l(P_{sx}+(x,y)+P_{yv})\ge l(P_{su}+(u,v))=l(P_{sv}) l(Psx+(x,y))≥l(Psu+(u,v)),所以l(P)=l(Psx+(x,y)+Pyv)≥l(Psu+(u,v))=l(Psv)
所以s到S中任意一点的路径距离都是最短的。
3 例题
问题描述
给定一张图,求第一个点和第N个点之间的最短路径。在这里,路径定义为该路径上所有边的乘积。
输入
第一行有两个数字N和M。
接下的M行,每行包含三个数字u,v,w,表示一条从u到v的有向路径,权值为w。
输出
输出最短路径的值,结果需要模19260817。
输入样例
3 2
1 2 3
2 3 4
输出样例
12
参考代码
import java.util.ArrayList;
import java.util.Scanner;
public class Main1 {
static class node {
ArrayList<Integer> childIndex = new ArrayList<>();
ArrayList<Integer> path = new ArrayList<>();
node() {
}
}
static class element {
int index;
double dist;
int preIndex;
element(int index, double dist, int preIndex) {
this.index = index;
this.dist = dist;
this.preIndex = preIndex;
}
}
public static void exchange(element[] pq, int i, int j) {
element temp = pq[i];
pq[i] = pq[j];
pq[j] = temp;
}
public static void raise(element[] pq, int tail) {
int k = tail;
while (k > 1 && pq[k / 2].dist > pq[k].dist) {
exchange(pq, k / 2, k);
k = k / 2;
}
}
public static void sink(element[] pq, int tail) {
int k = 1;
while (2 * k <= tail) {
int j = 2 * k;
if (j + 1 <= tail && pq[j + 1].dist < pq[j].dist) {
j++;
}
if (pq[k].dist > pq[j].dist) {
exchange(pq, k, j);
k = j;
} else {
break;
}
}
}
static int Mod = 19260817;
public static void main(String[] args){
Scanner in = new Scanner();
int n = in.nextInt();
int m = in.nextInt();
boolean[] isIn = new boolean[n + 1];
node[] nodeList = new node[n + 1];
int[] preNode = new int[n + 1];
for (int i = 1; i <= n; i++) {
nodeList[i] = new node();
}
for (int i = 0; i < m; i++) {
int index = in.nextInt();
nodeList[index].childIndex.add(in.nextInt());
nodeList[index].path.add(in.nextInt());
}
element[] pq = new element[n * 500];
pq[1] = new element(1, 0, -1);
int tail = 1;
while (!isIn[n]) {
while (isIn[pq[1].index]) {
exchange(pq, 1, tail);
tail--;
sink(pq, tail);
}
element outElement = pq[1];
node outNode = nodeList[pq[1].index];
preNode[outElement.index] = outElement.preIndex;
isIn[pq[1].index] = true;
if (isIn[n]) {
break;
}
exchange(pq, 1, tail);
tail--;
sink(pq, tail);
for (int i = 0; i < outNode.childIndex.size(); i++) {
if (!isIn[outNode.childIndex.get(i)]) {
tail++;
pq[tail] = new element(outNode.childIndex.get(i), Math.log(outNode.path.get(i)) + outElement.dist, outElement.index);
raise(pq, tail);
}
}
}
int now_index = n;
long result = 1;
while (now_index != 1) {
int pre_index = preNode[now_index];
node chooseNode = nodeList[pre_index];
int minPath = 1000000;
for (int i = 0; i < chooseNode.childIndex.size(); i++) {
if (chooseNode.childIndex.get(i) == now_index && chooseNode.path.get(i)<minPath) {
minPath = chooseNode.path.get(i);
}
}
result = (result * minPath%Mod) % Mod;
now_index = pre_index;
}
System.out.println(result);
}
}
eNode.path.get(i);
}
}
result = (result * minPath%Mod) % Mod;
now_index = pre_index;
}
System.out.println(result);
}
}