算法 {最短路}
最短路
性质
假如你要求: 哪些边 可以作为S->T
的最短路路径上的边;
.
在有向图G上 求完S->T
的最短路后, 再反着 对其反图GG 做BFS, Que.push(T)
, 令cur: Que.front(); nex: cur在GG里的邻接点
, 如果满足Dist[nex] == Dist[cur] - wth
那么就说明 G中nex->cur
这条边 可以是S->T
的最短路路径里的边;
.
例题: @LINK: https://leetcode.cn/contest/weekly-contest-394/problems/find-edges-in-shortest-paths/
;
算法
多起點
#定义#: 對於若干點s0,s1,s2,...,sN
, 對於任一點x
求出x->{s0/s1/.../sN}
的最短路;
#暴力解#: 假設以一點為起點 求最短路的耗時是X1
, 那麽暴力做法是O(所有點 * X1)
, 即以每一個點為起點 求最短路, 比如當前點x
跑最短路 他的答案是min( dist[s0], dist[s1], ...)
便是x
點的答案;
#最優解#: 假如我們有a->b的最短路 == b->a的最短路
(也就是圖為無向圖), 那麽我們只需 以s0,s1,...
為起點 跑一次最短路 得到的dist[x]
就是x
點的答案;
例題
@LINK: https://editor.csdn.net/md/?articleId=136283729
;
多起點求BFS_1;
笔记
定义
@MARK_1
给定一个{有向/无向}图 起点
S
S
S 终点T, 对于任何一条从起点出发的路径 都有一个权值 , 答案为: 所有
S
→
T
S\to T
S→T路径的权值 最大值/最小值;
.
比如
S
→
T
S\to T
S→T有3条路径 他们权值为:
[
a
,
b
,
c
]
[a,b,c]
[a,b,c], 那么 答案为max或min( a,b,c)
;
.
权值计算公式是任意的, 比如一条路径是:
S
→
a
→
b
→
c
S\to a \to b \to c
S→a→b→c, 他们的权值 可能是这4个点权的{和/ 最值} 可能是这3条边权的{和/ 最值} 或者边权点权都混合, 总之就是个计算公式;
.
虽然路径的 权值计算公式是任意的, 但答案的计算公式 不是任意的, 他只有2种选择: {最大值/ 最小值}, 即在所有
S
→
.
.
.
→
T
S\to...\to T
S→...→T的路径里 找{最大/最小}的权值;
例题
https://mp.csdn.net/mp_blog/creation/success/130383675
.
路径的权值为: max( 所有点权)
, 然后求最短路; (即一个min
一个max
);
最值路径分析
上下文,预备知识
Context
let’s consider a graph with both negative/positive edges (implies that the only feasible algorithm is SPFA), and calculate the Greatest-Path (all notions on it can also be deduced to adapt the Least-Path)
That is, what we are talking about is all about the Greatest-Path.
Solvable
This notion is not absolute, cuz a Positive-Loop maybe not accessible from some start-points; that is, if the start-points is the whole points, then it must be Un-Solvable if there exists a Loop;
However, if the start-point is
a
a
a and
a
a
a can reach any Loop although there exists loop, then it is Solvable; the points on Loop is Un-Reachable;
In summary, when we talking about the Solvability, it must be linked to what the Start-points are.
起点的初始化
所有起点的初始值均相同
Let S S S be the set of start-points, when we set D i s t [ S ] = 0 Dist[ S] = 0 Dist[S]=0 originally (note that all values are equal, that is vital), after performing SPFA, if x x x is accessible then D i s t [ x ] Dist[x] Dist[x] actually means the sum of edges on the optimal-path. Cuz according to the definition of D i s t Dist Dist, D i s t [ x ] = D i s t [ s ] + w Dist[x] = Dist[s] + w Dist[x]=Dist[s]+w where s ∈ S s \in S s∈S moreover s s s is a real-start-point therefore D i s t [ s ] = 0 Dist[s] = 0 Dist[s]=0, and w w w are the sum of edges.
s
∈
S
s \in S
s∈S and
s
s
s is real-start-point, then
D
i
s
t
[
s
]
=
0
Dist[s] = 0
Dist[s]=0; otherwise it is start-point but not real, then
D
i
s
t
[
s
]
>
0
Dist[s] > 0
Dist[s]>0 (cuz the initial value
D
i
s
t
[
s
]
=
0
Dist[s] = 0
Dist[s]=0, only a value which
>
0
> 0
>0 can update it); for
x
∉
S
x \notin S
x∈/S and is accessible, then
D
i
s
t
[
x
]
=
(
−
/
0
/
+
)
Dist[x] = (-/0/+)
Dist[x]=(−/0/+) that is arbitrary.
(as a lemma, if set
D
i
s
t
[
a
l
l
]
=
d
Dist[all] = d
Dist[all]=d originally, then after SPFA, we have
D
i
s
t
[
a
l
l
]
≥
d
Dist[all] \geq d
Dist[all]≥d; for the real-start-points
s
∈
S
s \in S
s∈S such that
D
i
s
t
[
a
]
=
d
Dist[a] = d
Dist[a]=d; for any point
a
a
a, the best-path denoted by
D
i
s
t
[
a
]
Dist[a]
Dist[a]
s
→
.
.
.
→
a
s \to ... \to a
s→...→a may contains Negative-Edges although
D
i
s
t
[
a
]
≥
d
Dist[a] \geq d
Dist[a]≥d and
a
a
a is start-points)
So given a
D
i
s
t
[
x
]
=
c
Dist[x] = c
Dist[x]=c and
x
x
x is not a real-start-point, it means a path
s
→
.
.
.
→
x
s \to ... \to x
s→...→x and
c
c
c equals to the sum of all edges.
D
i
s
t
[
s
]
=
c
Dist[s] = c
Dist[s]=c where
s
s
s is a real-start-point, then we know that
c
=
0
c = 0
c=0, it means a empty path (cuz there is no edge on a empty-path, so the sum of all edges by
0
0
0)
Let the answer
D
i
s
t
[
]
Dist[]
Dist[] above be a new name
P
a
t
h
[
]
Path[]
Path[] (as we showed above, it actually denotes the sum of edges on the path, when
D
i
s
t
[
S
]
=
0
Dist[S]=0
Dist[S]=0 originally).
If you set
D
i
s
t
[
S
]
=
d
Dist[S] = d
Dist[S]=d originally (note that all values are still the same), after SPFA, then
D
i
s
t
[
x
]
=
P
a
t
h
[
x
]
+
d
Dist[x] = Path[x] + d
Dist[x]=Path[x]+d for all points
x
x
x.
Proof: @ToDo
The difference between ( D i s t [ a l l ] = 0 Dist[all] = 0 Dist[all]=0 originally) and ( D i s t [ s o m e ] = 0 Dist[some] = 0 Dist[some]=0 originally) is that, the path of x x x can be empty in the former case (that is, no path a → . . . → x a \to ... \to x a→...→x with length w w w can update D i s t [ x ] = 0 Dist[x] = 0 Dist[x]=0, due to w ≤ 0 w \leq 0 w≤0), however the path of x x x (where x x x is not a start-point) must not be empty if and only if D i s t [ x ] ≠ i n v a l i d Dist[x] \neq invalid Dist[x]=invalid.
所有起点的初始值不相同
无解 与 起点初始化 的关系
If a graph exists a Positive-Loop (this can be tested by set D i s t [ a l l ] = d Dist[ all] = d Dist[all]=d originally, and then SPFA), then for any start-points set S S S which is Un-Solvable (that is, there exists s ∈ S s \in S s∈S that can reach at a Positive-Loop), whatever the initial-value D i s t [ S ] Dist[S] Dist[S] be (maybe all equals to d d d which is a arbitrary number, or maybe distinct D i s t [ s 1 ] = x , D i s t [ s 2 ] = y , . . . Dist[s1] = x, Dist[s2] = y, ... Dist[s1]=x,Dist[s2]=y,...), It is always Un-Solvable.
权值为 0 0 0的边
For a 0 0 0 weighted edge a → b a \to b a→b and a a a is accessible, then we always have D i s t [ b ] ≥ D i s t [ a ] Dist[ b] \geq Dist[a] Dist[b]≥Dist[a]. (note that the context mentions above assuming that we are handling the Greatest-Path)
As a lemma, for a graph which contains a Zero-Chain
0
→
1
→
2...
→
n
0 \to 1 \to 2 ... \to n
0→1→2...→n where each edge is
0
0
0.
Now if we set
D
i
s
t
[
S
]
=
C
Dist[ S] = C
Dist[S]=C where
0
∈
S
0 \in S
0∈S and
C
C
C is a arbitrary number, after SPFA, then we will have a non-decreasing sequence
D
i
s
t
[
0
]
≤
D
i
s
t
[
1
]
≤
.
.
.
≤
D
i
s
t
[
n
]
Dist[0] \leq Dist[1] \leq ... \leq Dist[n]
Dist[0]≤Dist[1]≤...≤Dist[n].
Also,
D
i
s
t
[
0
]
Dist[0]
Dist[0] must equals
C
C
C, otherwise this graph is unsolvable;
Proof: When
D
i
s
t
[
0
]
≠
C
Dist[0] \neq C
Dist[0]=C (it must
>
C
> C
>C), then there is a path
s
→
.
.
.
→
t
→
0
s \to ... \to t \to 0
s→...→t→0 where the sum of edges are
>
0
> 0
>0 and
s
s
s is a real-start-point therefore
D
i
s
t
[
s
]
=
C
Dist[s] = C
Dist[s]=C; cuz there is always a path
0
→
.
.
.
→
s
0 \to ... \to s
0→...→s whose sum of edges is
0
0
0, so we also have a path
0
→
.
.
.
→
s
→
.
.
.
→
0
0 \to ... \to s \to ... \to 0
0→...→s→...→0 whose sum of edges is
>
0
> 0
>0 (a positive-loop means No-Solution)
But the inverse is not true (G is unsolvable, not implies
D
i
s
t
[
0
]
≠
C
Dist[0] \neq C
Dist[0]=C);
Proof: 0 -> 1, 1 -> 2
with edge-weight be
0
0
0, another edge 2 -> 1
with weight
1
1
1, so 1-2
is a positive-loop, therefore No-Solution, but
D
i
s
t
[
0
]
=
D
Dist[0] = D
Dist[0]=D
最短路Shortest-Path
Overview
The best-path problem is that, given a weighted graph G G G, a set of start-points S S S and D i s t [ ] Dist[] Dist[] which means the distance of the best-path.
There are some crucial notions:
+
G
G
G is valid / solvable if and only if every
D
i
s
t
[
]
Dist[]
Dist[] is either No-Path or unique.
+
A Path is
s
→
.
.
.
→
a
s \to ... \to a
s→...→a where
s
∈
S
s \in S
s∈S.
+
The Distance of a Path
s
→
a
→
b
→
t
s \to a \to b \to t
s→a→b→t which
s
∈
S
s \in S
s∈S equals
D
i
s
t
[
s
]
+
W
e
i
g
h
t
[
s
→
a
]
+
W
e
i
g
h
t
[
a
→
b
]
+
W
e
i
g
h
t
[
b
→
t
]
Dist[s] + Weight[s\to a] + Weight[a \to b] + Weight[ b \to t]
Dist[s]+Weight[s→a]+Weight[a→b]+Weight[b→t].
+
A binary-operation
+
+
+ on
G
G
G which allows the operation
D
i
s
t
[
x
]
+
W
e
i
g
h
t
[
e
]
Dist[x] + Weight[e]
Dist[x]+Weight[e] where
x
x
x is point and
e
e
e is a edge
x
→
y
x \to y
x→y, it can be used to update
D
i
s
t
[
y
]
Dist[y]
Dist[y] (
+
+
+ is not necessary the addition). Essentially speaking,
D
i
s
t
[
x
]
Dist[x]
Dist[x] is a path from any start-point to
x
x
x, so
D
i
s
t
[
x
]
+
W
e
i
g
h
t
[
e
]
Dist[x] + Weight[e]
Dist[x]+Weight[e] is a path where a edge
e
e
e be appended to the path
D
i
s
t
[
x
]
Dist[x]
Dist[x] and
y
y
y be its end-point in the path.
+
A binary-relation
<
<
< on
G
G
G allows two
D
i
s
t
[
]
Dist[]
Dist[] can be compared. Two paths of
a
a
a:
s
1
→
.
.
.
→
a
s1 \to ... \to a
s1→...→a with Distance
D
1
D1
D1 and
s
2
→
.
.
.
→
a
s2 \to ... \to a
s2→...→a with Distance
D
2
D2
D2 where
s
1
,
s
2
∈
S
s1,s2 \in S
s1,s2∈S. The former path is better than the latter if and only if
D
1
<
D
2
D1 \lt D2
D1<D2.
+
The best-distance of a point
x
x
x is
D
i
s
t
[
x
]
Dist[x]
Dist[x], it is unique if the graph is valid. It is equivalent to the minimal-number of the distances of all paths of
x
x
x.
+
The best-path of a point
x
x
x is path with the distance
D
i
s
t
[
x
]
Dist[x]
Dist[x], it maybe not unique.
The next algorithm is used to check whether a graph is valid (solvable):
- Firstly, initialize D i s t [ x ] = I x , x ∈ S Dist[ x] = I_x, x \in S Dist[x]=Ix,x∈S with certain values ( I x I_x Ix depends on specific question, it can be arbitrary number and be distinct to each other), while other D i s t [ x ] Dist[ x] Dist[x] will be Infinity with respect to < < < meaning that there is no path from any point of S S S to x x x (any non-Infinity D i s t [ a ] Dist[a] Dist[a] add a edge W e i g h t [ a → b ] Weight[a\to b] Weight[a→b] can always be used to update D i s t [ b ] Dist[b] Dist[b] if D i s t [ b ] = I n f i n i t y Dist[b] = Infinity Dist[b]=Infinity.)
- Secondly, choose two points x , y x, y x,y arbitrarily (maybe same) and choose a arbitrary edge e e e from x → y x \to y x→y, then update D i s t [ y ] = D i s t [ x ] + e Dist[y] = Dist[x] + e Dist[y]=Dist[x]+e if D i s t [ x ] + e < D i s t [ y ] Dist[x] + e < Dist[y] Dist[x]+e<Dist[y]. Perform this operation infinitely until “there is no such x , y x,y x,y and a edge e e e between them satisfying D i s t [ y ] Dist[y] Dist[y] can be updated by D i s t [ x ] Dist[x] Dist[x] and e e e.”
Set( Dist, Infinity);
for( auto s : S){
Dist[ s] = I[s];
}
while( true){
succ = false;
for( a = 1; a <= n; ++a){
for( e is every edge from a){
b: the endpoint of e
if( Dist[ a] + Weight[ e] < Dist[ b]){
Dist[ b] = Dist[ a] + Weight[ e];
succ = true;
}
}
}
if( false == succ){ break;}
}
In fact, SPFA is the generalization of this idea.
Properties
+
If
G
G
G is valid, there must exists a best-path which has no loop (the points on the path are pairwise distinct) for a accessible point
a
a
a. (because there maybe zero-loop in which
G
G
G is still valid, the best-paths of a point maybe infinite)
In other words, if there is zero-loop in a best-path, we remove all zero-loop, then the distance of the path is still unchanged, and eventually the points on the path are pairwise distinct.
+
Any best-path is a path whose first-point must belongs to
S
S
S, that is, its first-point must be a start-point;
.
this can be proved by the definition of path.
+
Let the length of a path be the count of edges on the path. The length of any best-path of
a
∉
S
a \notin S
a∈/S must
>
0
\gt 0
>0 (according to the definition of path)
There exists at least one start-point
s
∈
S
s \in S
s∈S where
D
i
s
t
[
s
]
=
T
[
s
]
Dist[ s] = T[s]
Dist[s]=T[s], subsequently the length of the best-path of
s
s
s
=
0
= 0
=0
The best-path of a start-point maybe not itself. (e.g.,
1
→
2
1 \to 2
1→2 with weight 1
, if the start-points are I[1] = 1, I[2] = 10
, the best-path of 1
is 1
with the distance I[1] = 1
, but the best-path of 2
is 1 -> 2
with distance I[1] + 1 = 2 != I[2]
.
We call a start-point with
D
i
s
t
[
x
]
=
I
[
x
]
Dist[ x] = I[ x]
Dist[x]=I[x] is real-start-point.
As the best-path of any point is determined, either it is the form s
of a single point (s
is real-start-point) or of the form s -> ... -> a
(s
is real-start-point, otherwise it objects to the Divisibility-Property)
So, there must exist at least one real-start-point.
+
The count of best-path of a real-start-point must
=
1
= 1
=1 (one point that is itself and no edge)
The count of best-path of a non real-start-point is
≥
1
\geq 1
≥1.
+
Divisibility
For a best-path of
x
x
x, e.g.,
s
→
a
→
b
→
x
s \to a \to b \to x
s→a→b→x, any of its prefix is also a best-path.
.
s,a,b,x
is a best-path of x
; s,a,b
is a best-path of b
; s,a
is a best-path of a
;
s
∈
S
s \in S
s∈S.
In other words, from this best-path we have:
D
i
s
t
[
x
]
=
D
i
s
t
[
b
]
+
W
e
i
g
h
t
[
b
→
x
]
=
D
i
s
t
[
a
]
+
W
e
i
g
h
t
[
a
→
b
]
+
W
e
i
g
h
t
[
b
→
x
]
=
D
i
s
t
[
s
]
+
W
e
i
g
h
t
[
s
→
a
]
+
W
e
i
g
h
t
[
a
→
b
]
+
W
e
i
g
h
t
[
b
→
x
]
Dist[ x] \\ = Dist[ b] + Weight[b\to x] \\ = Dist[ a] + Weight[ a \to b] + Weight[b\to x] \\ = Dist[ s] + Weight[ s \to a] + Weight[ a \to b] + Weight[b\to x]
Dist[x]=Dist[b]+Weight[b→x]=Dist[a]+Weight[a→b]+Weight[b→x]=Dist[s]+Weight[s→a]+Weight[a→b]+Weight[b→x]
BFS-01
Given a weighted graph with the weight of all edges is either
0
0
0 or
1
1
1.
If a path is
s
→
a
→
b
→
c
s \to a \to b \to c
s→a→b→c (where
s
s
s is the start, the weight of these
3
3
3 edges are
w
a
,
w
b
,
w
c
wa, wb, wc
wa,wb,wc), then the distance of
c
c
c in the path is
w
a
+
w
b
+
w
c
wa + wb + wc
wa+wb+wc (the sum of the edges weight in the path).
Finally,
D
i
s
t
a
n
c
e
[
c
]
Distance[ c]
Distance[c] means the minimal distance of all paths that
s
→
.
.
.
→
c
s \to ... \to c
s→...→c.
It can be solved by Dijkstra, but there is simpler and more effective algorithm BFS-01.
ptr_distance[ _start_point] = 0;
que.push_back( _start_point);
int cur, nex, edge, wth;
while( false == que.empty()){
cur = que.front();
que.pop_front();
if( cur == _end_point){ break;}
if( ptr_isFirstTime[ cur]){ continue;}
ptr_isFirstTime[ cur] = true;
for( edge = ptrRef_graph->Head[ cur]; ~edge; edge = ptrRef_graph->Next[ edge]){
nex = ptrRef_graph->Vertex[ edge];
wth = ( ptrRef_graph->Weight[ edge] <= _flag ? 0 : 1);
if( ptr_distance[ cur] + wth < ptr_distance[ nex]){
ptr_distance[ nex] = ptr_distance[ cur] + wth;
if( wth == 0){
que.push_front( nex);
}
else{
que.push_back( nex);
}
}
}
}
Proof:
- Similar as BFS-1, we divide all points into several layers/sets according to their distance (answer),
d
e
p
t
h
[
0
]
=
A
,
d
e
p
t
h
[
1
]
=
B
,
C
,
D
,
.
.
.
depth[0] = A, depth[1] = B, C, D, ...
depth[0]=A,depth[1]=B,C,D,... (start-point
s
∈
A
s \in A
s∈A)
For a update x → y x \to y x→y with weight w w w, if w = 0 w = 0 w=0 we put y y y to the front of deque, otherwise put it to the back.
Let’s define a sequence S S S that consists of the points firstly get out of the deque, [ s , . . . ] [s, ...] [s,...] (the head must be s s s)
Firstly, prove that A A A must be a prefix of S S S
Disproof:
+
If not, then S S S likes [ p r e ( 0 ) , c ( 1 ) , s u f ( 0 ) ] [pre(0), c(1), suf(0)] [pre(0),c(1),suf(0)] ( S = p r e , A = p r e + s u f S = pre, A = pre+suf S=pre,A=pre+suf, c ( 1 ) c(1) c(1) means that when it get out of deque, its distance is 1 1 1), we need to prove s u f = ∅ suf = \empty suf=∅
+
For a point a ∈ s u f a \in suf a∈suf, there must has a path s → b 1 → b 2 → a s \to b1 \to b2 \to a s→b1→b2→a with all edges equal to 0 0 0.
Because s ∈ p r e s \in pre s∈pre ( s s s is ahead of c c c always satisfies), so we have [ s , c ] [s, c] [s,c].
b 1 b1 b1 will also ahead of c c c (due to there is a edge s → b 1 s \to b1 s→b1 with weight 0 0 0, so b 1 b1 b1 will put the front of deque, while c c c is updated by a weight 1 1 1 and then putted in the back), so we have [ s , b 1 , c ] [s, b1, c] [s,b1,c]
b 2 b2 b2 will also ahead of c c c (the analysis is similar to b 1 b1 b1), so we have [ s , b 1 , b 2 , c ] [s, b1, b2, c] [s,b1,b2,c]
a a a will also ahead of c c c (the analysis is similar to b 1 b1 b1), so we have [ s , b 1 , b 2 , a , c ] [s, b1, b2, a, c] [s,b1,b2,a,c]
Finally, we have S u f Suf Suf should always be ahead of c c c.
In summary, S = [ A , B , C , . . . ] S = [A, B, C, ...] S=[A,B,C,...]
It shares some properties with BFS-1, but also they have some differences.
- A points may enter the queue more than once. e.g., s → a s \to a s→a (weight is 1 1 1) and s → b → a s \to b \to a s→b→a (weights are 0 0 0), then s s s will put a a a into the queue with d i s t a n c e [ a ] = 1 distance[a] = 1 distance[a]=1 then d i s t a n c e [ b ] = 0 distance[b] = 0 distance[b]=0 also into the queue, and finally the queue is [ b , a ] [b, a] [b,a]. when b b b out of queue, it will get d i s t a n c e [ a ] = 0 distance[a] = 0 distance[a]=0 then a a a into the queue again, now the queue is [ a , a ] [a, a] [a,a]
- When we need to pop-front a element of the deque, at this time, if we left the first element and clear all other elements for the same point in the queue, e.g., the queue is like
[
a
‾
,
b
‾
,
b
,
a
,
c
‾
,
b
,
c
]
[\overline {a}, \overline {b}, b, a, \overline {c}, b, c]
[a,b,b,a,c,b,c] then the sequence is
[
a
‾
,
b
‾
,
c
‾
]
[\overline {a}, \overline {b}, \overline {c}]
[a,b,c], let the distance of the first point is
x
=
d
i
s
t
a
n
c
e
[
a
]
x = distance[a]
x=distance[a], that of the last element is
y
y
y, then
x
=
y
x = y
x=y or
x
=
y
−
1
x = y - 1
x=y−1, and the sequence is the form
[
x
,
.
.
.
,
x
,
y
,
.
.
.
,
y
]
[x,...,x, y,...,y]
[x,...,x,y,...,y].
the distance of a a a (first element) must equals x x x, but the distance of b / c / . . b/c/.. b/c/.. maybe the current distance y y y (e.g., x x x will update b / c b/c b/c to x x x when y = x + 1 y = x + 1 y=x+1).
DAG
Given a DAG, because there are only finite paths between any two points, that is, the count of paths with the form of
a
→
.
.
.
→
b
a \to ... \to b
a→...→b is finite (because DAG has no loop, any path would not contains a same edge more than once, i.e., the path
a
→
b
→
c
.
.
.
→
b
→
c
.
.
.
a \to b \to c ... \to b \to c ...
a→b→c...→b→c... is impossible in DAG (this is not matters of Shortest-Path, it is the nature of DAG)
Therefore, the weight of edge is arbitrary (maybe negative), the optimal-path is also arbitrary (maybe shortest or longest)
In fact, DAG is the nature of DP, so the approach here resembles to DP)
Firstly, get the Topo-Path of DAG
A
1
,
A
2
,
.
.
.
A1, A2, ...
A1,A2,..., calculate the optimal-path from
A
s
→
A
t
As \to At
As→At (if
s
>
t
s > t
s>t, there is No-Path)
Initially, set
D
i
s
t
[
A
s
]
=
0
Dist[ As] = 0
Dist[As]=0 with other
D
i
s
t
=
I
n
v
a
l
i
d
Dist = Invalid
Dist=Invalid, iterate the Topo-Sequence
A
1
,
.
.
.
A1, ...
A1,...
+
If Dist[ cur] == Invalid
, continue to the next points.
+
Otherwise, for a edge
c
u
r
→
n
e
x
cur \to nex
cur→nex with weight
w
w
w, try to update
D
i
s
t
[
n
e
x
]
=
D
i
s
t
[
c
u
r
]
+
w
Dist[ nex] = Dist[ cur] + w
Dist[nex]=Dist[cur]+w.
There is a prominent property here,
D
i
s
t
[
c
u
r
]
Dist[ cur]
Dist[cur] is absolutely correct. That means if the optimal path of
a
a
a is the form of
.
.
.
c
u
r
,
a
... cur, a
...cur,a, then it must can be updated at here
D
i
s
t
[
a
]
=
D
i
s
t
[
c
u
r
]
+
e
Dist[ a] = Dist[cur] + e
Dist[a]=Dist[cur]+e.
Dijkstra
Based on Overview, if all W e i g h t ≥ 0 Weight \geq 0 Weight≥0, Dijkstra can be used.
There are some terms:
+
A Standard-Dijkstra-Graph which consists of all edges with
≥
0
\geq 0
≥0 weight.
+
A Positive-Dijkstra-Graph is Standard-Dijkstra-Graph removing all edges of which the weight is
0
0
0.
Positive-Dijkstra-Graph
Assuming that all weights of edges are
>
0
\gt 0
>0, and
G
G
G is valid.
Let the start-points be
S
S
S, and the real-start-points be
R
S
RS
RS.
The best-path of any (non real-start-point) point
a
a
a must be the form
s
→
.
.
.
→
b
→
a
s \to ... \to b \to a
s→...→b→a where
s
∈
R
S
s \in RS
s∈RS and the length
>
0
\gt 0
>0.
We have
D
i
s
t
[
b
]
<
D
i
s
t
[
a
]
Dist[ b] < Dist[a]
Dist[b]<Dist[a] (all edges are positive), called
b
b
b is a previous-point of
a
a
a.
Let
P
r
e
(
x
)
Pre( x)
Pre(x) be a set satisfying
D
i
s
t
[
y
]
+
W
e
i
g
h
t
[
y
→
x
]
=
D
i
s
t
[
x
]
Dist[ y] + Weight[y \to x] = Dist[ x]
Dist[y]+Weight[y→x]=Dist[x] for any
y
∈
P
r
e
(
x
)
y \in Pre(x)
y∈Pre(x).
It can be proved that, if
y
∈
P
r
e
(
x
)
y \in Pre(x)
y∈Pre(x), it is impossible that
x
∈
P
r
e
(
y
)
x \in Pre( y)
x∈Pre(y)
Dijkstra-Algorithm (Heap)
A structure heap< pair< A, B> >
where a item (a pair<A,B>
) denotes a path,
A
A
A denotes the distance of this path,
B
B
B denotes the endpoint of this path. The ordering of Heap is optimization (here is
<
<
<)
1
pus all starts-point into heap
Set( Dist, infinity);
for( s : S){
Dist[ s] = I[ s];
heap.push( {Dist[s], s});
}
2
try to update the adjacent-points
while( heap is not empty){
dist = heap.top().first; //< must equals to `Dist[ cur]`
cur = heap.top().second;
heap.pop();
if( `cur` is not the first time met){
continue;
}
for( `e` be all adjacent-edges of `cur`){
`nex` be the endpoint of `e`
if( dist + Weight[ e] < Dist[ e]){
Dist[ nex] = dist + Weight[ e];
heap.push( {Dist[nex], nex});
}
}
}
That is the whole of Dijkstra-Algorithm.
All cur
points forms a sequence where may contains same points, then we only leave the first occurrence for the same points, call this new sequence
S
e
q
Seq
Seq.
There is a theorem that all
P
r
e
(
x
)
Pre(x)
Pre(x) will be ahead of
x
x
x in
S
e
q
Seq
Seq.
Disprove:
.
Let
y
∈
P
r
e
(
x
)
y \in Pre(x)
y∈Pre(x), we have
D
i
s
t
[
y
]
<
D
i
s
t
[
x
]
Dist[y] < Dist[x]
Dist[y]<Dist[x]; if ... x ... y ...
in
S
e
q
Seq
Seq.
.
When the heap is x ...
(the top is
x
x
x), y
must not occurs in ...
(due to Dist[y] < Dist[x]
)
.
Therefore, there must be a point in x ...
that updates y
(cause that y
be putted in heap), but the distance of any point in x ...
is
>
>
>
D
i
s
t
[
y
]
Dist[y]
Dist[y]. So, y
will never occurs back of x
.
Consequently, we can gather all best-paths for any point, because P r e ( x ) Pre(x) Pre(x) is ahead of x x x in S e q Seq Seq.
Standard-Dijkstra-Graph
Now, the best-paths of a point maybe infinite if exists zero-loop, but it would not affect the D i s t [ ] Dist[] Dist[] ( 0 0 0 will not make a path better). Although there is no zero-loop, it differs from the case above in some aspects.
Using the same Dijkstra-Algorithm to get a
S
e
q
Seq
Seq, it can be proved that
D
i
s
t
[
]
Dist[]
Dist[] is still correct, but
P
r
e
(
x
)
Pre(x)
Pre(x) is ahead of
x
x
x in
S
e
q
Seq
Seq does not satisfies. Because for two best-paths
.
.
.
→
x
→
y
... \to x \to y
...→x→y and
.
.
.
→
y
→
x
... \to y \to x
...→y→x which the weight of
x
→
y
x\to y
x→y and
y
→
x
y\to x
y→x are both
0
0
0, so it shows that
x
∈
P
r
e
(
y
)
x \in Pre(y)
x∈Pre(y) and
y
∈
P
r
e
(
x
)
y \in Pre(x)
y∈Pre(x), as a result, it is impossible to form a sequence.
There are some theorems which are not agree with the case of Positive-Dijkstra-Graph.
+
For any
y
∈
P
r
e
(
x
)
y \in Pre(x)
y∈Pre(x) which
D
i
s
t
[
y
]
<
D
i
s
t
[
x
]
Dist[y] < Dist[x]
Dist[y]<Dist[x], then
y
y
y must ahead of
x
x
x in
S
e
q
Seq
Seq.
+
If for all
y
∈
P
r
e
(
x
)
y \in Pre(x)
y∈Pre(x) satisfies
D
i
s
t
[
y
]
=
D
i
s
t
[
x
]
Dist[y] = Dist[x]
Dist[y]=Dist[x], then there must exists at least one
y
∈
P
r
e
(
x
)
y \in Pre(x)
y∈Pre(x) that ahead of
x
x
x in
S
e
q
Seq
Seq.
Therefore, it is impossible that gathering all best-paths of a point.
For instance, s -> a, s -> b -> a
, all weight are 2
but the weight of b->a
is 0
. The sequence would be s, a, b
, it will be
D
i
s
t
[
s
]
=
I
[
s
]
,
D
i
s
t
[
a
]
=
D
i
s
t
[
s
]
+
2
,
D
i
s
t
[
b
]
=
D
i
s
t
[
s
]
+
2
Dist[s]= I[s], Dist[a] = Dist[s] + 2, Dist[b] = Dist[s] + 2
Dist[s]=I[s],Dist[a]=Dist[s]+2,Dist[b]=Dist[s]+2. Due to the best-path s -> b -> a
, when we at b
, its next-point a
actually is ahead of b
. This differs from the above, but it will not affect the correctness of
D
i
s
t
[
]
Dist[]
Dist[].
Inferior-Best Path
When
G
G
G is a standard Dijkstra-Graph (i.e., with the weight
≥
0
\geq 0
≥0 for all edges), the definition of Inferior-Best Path has two kinds:
1
<
<
< Best-Path, that is, the inferior-best-path differs (from the aspect of the edges on the path) from best-path and their distance are also different.
2
≤
\leq
≤ Best-Path, that is, the inferior-best-path differs (from the aspect of the edges on the path) from best-path and their distance maybe the same.
(e.g., the distance of the paths of a point: 1 ... 1 2 ... 2
. If the count of 1
is
≥
2
\geq 2
≥2, the Type-1 inferior-path is
1
1
1, otherwise 2
; If the count of 2
is
>
0
\gt 0
>0, the Type-2 Inferior-path is 2
)
Let
e
e
e be any edge whose end-point is
y
y
y, and
x
x
x be the start-point of
e
e
e,
D
i
s
t
[
]
Dist[]
Dist[] denotes the distance of best-path and
D
i
s
t
2
[
]
Dist2[]
Dist2[] denotes inferior-path.
The distance of inferior-path of
y
y
y must derives from,
D
i
s
t
[
x
]
+
e
Dist[x] + e
Dist[x]+e or
D
i
s
t
2
[
x
]
+
e
Dist2[x] + e
Dist2[x]+e (if it derives from
D
i
s
t
3
[
x
]
Dist3[x]
Dist3[x], then
D
i
s
t
3
[
x
]
+
e
Dist3[x] + e
Dist3[x]+e would not be the No.2 best-path, because
D
i
s
t
[
x
]
<
D
i
s
t
1
[
x
]
<
D
i
s
t
[
2
]
Dist[x] < Dist1[x] < Dist[2]
Dist[x]<Dist1[x]<Dist[2] (this is satisfied whatever Type-1 or Type-2)
Because all D i s t [ ] Dist[] Dist[] in S e q Seq Seq are correct, so it can be used to updated all inferior-path of the form D i s t [ x ] + e Dist[x] + e Dist[x]+e.
The next algorithm is based on Type-1 (Type-2 will be showed in annotation)
Set( Dist, Infinity);
Set( Dist2, Infinity);
{ // update `Dist[x] + e -> Dist2[y]`
for( s : S){
Dist[ s] = I[ s];
heap.push( {Dist[s], s});
}
while( heap is not empty()){
cur = heap.top().second;
heap.pop();
if( `cur` is not the first time met){
continue;
}
for( e : adjacent-edges of `cur`){
nex = adjacent-point of `e`
if( Dist[ cur] + e < Dist[ nex]){
Dist2[ nex] = Dist[ nex];
//< Vital. due to `Dist[x] >= Dist2[x]`, once `Dist[x]` has been updated, the previous `Dist[x]` must becomes the new `Dist2[x]`
//--
Dist[ nex] = Dist[cur] + e;
heap.push( {Dist[nex], nex});
}
else if( Dist[ cur] + e < Dist2[ nex]){
//< In the case `Type-2`, you should add a additional condition `Dist[cur] + e < Dist[ nex]`
Dist2[ nex] = Dist[ cur] + e;
}
}
}
}
{ // update `Dist2[x] + e -> Dist2[y]`
for( i : all points){
if( Dist2[ i] is not infinity){
heap.push( {Dist2[ i], i});
}
}
while( heap is not empty()){
cur = heap.top();
heap.pop();
if( cur is not the first time met){
continue;
}
for( e : adjacent-edges of `cur`){
nex = adjacent-point of `e`
if( Dist2[ cur] + e < Dist2[ nex]){
Dist2[ nex] = Dist2[cur] + e;
heap.push( {Dist2[ nex], nex});
}
}
}
}
In fact, we can merge the two procedures into a whole by checking the met-times; i.e., the pair {a, b}
where a
maybe denotes the best-path also maybe denotes the inferior-best-path depending on the the met-times of b
.
Although they had been merged, in fact they are still two discrete steps: one handles the case Dist -> Dist2
(first-time-met), another handles the case Dist2 -> Dist2
(second-time)
Set( Dist, Infinity);
Set( Dist2, Infinity);
{ // update `Dist -> Dist2` and `Dist2 -> Dist2` simultaneously
for( s : S){
Dist[ s] = I[ s];
heap.push( {Dist[s], s});
}
while( heap is not empty()){
cur = heap.top().second;
heap.pop();
if( the met-times of `cur` > 2){
continue;
}
for( e : adjacent-edges of `cur`){
nex = adjacent-point of `e`
if( the met-times of `cur` is 1){
if( Dist[ cur] + e < Dist[ nex]){
Dist2[ nex] = Dist[ nex];
//< Vital. due to `Dist[x] >= Dist2[x]`, once `Dist[x]` has been updated, the previous `Dist[x]` must becomes the new `Dist2[x]`
//--
Dist[ nex] = Dist[cur] + e;
heap.push( {Dist[nex], nex});
}
else if( Dist[ cur] + e < Dist2[ nex]){
//< In the case `Type-2`, you should add a additional condition `Dist[cur] + e < Dist[ nex]`
Dist2[ nex] = Dist[ cur] + e;
heap.push( {Dist2[ nex], nex});
}
}
else{ //< the met-times of `cur` is 2
if( Dist2[ cur] + e < Dist2[ nex]){
Dist2[ nex] = Dist[ cur] + e;
heap.push( {Dist2[ nex], nex});
}
}
}
}
}
Paths-Count
Only if
G
G
G is Positive-Dijkstra-Graph, it can be used to calculate Paths-Count. (because if
G
G
G has zero-loop which not affects
D
i
s
t
[
]
Dist[]
Dist[] but causes the Paths-Count will be infinite; even if there is no zero-loop, a best-path s -> x -> y
where the weight of x->y
is
0
0
0, however,
y
y
y maybe ahead of
x
x
x (e.g., there is a edge s -> y
with the same weight of s -> x
, then x, y
have the same
D
i
s
t
Dist
Dist, the order of them is arbitrary), consequently, when y
out of heap-queue, its
D
i
s
t
Dist
Dist is correct but
P
a
t
h
s
C
o
u
n
t
PathsCount
PathsCount is wrong.
So, when you need to calculate Paths-Count, make sure G G G is a Positive-Dijkstra-Graph.
Set( Dist, infinity);
for( s : S){
Dist[ s] = I[ s];
PathsCount[ s] = 1;
heap.push( {Dist[s], s});
}
while( heap is not empty){
cur = heap.top().second;
heap.pop();
if( `cur` is not the first time met){
continue;
}
for( `e` be all adjacent-edges of `cur`){
`nex` be the endpoint of `e`
if( Dist[ cur] + Weight[ e] < Dist[ nex]){
Dist[ nex] = Dist[cur] + Weight[ e];
PathsCount[nex] = PathsCount[cur];
heap.push( {Dist[e], e});
}
else if( Dist[cur] + Weight[ e] == Dist[ nex]){
PathsCount[nex] += PathsCount[cur];
}
}
}
Every pair (x,y)
where
x
∈
P
r
e
(
y
)
x \in Pre(y)
x∈Pre(y) in Positive-Dijkstra-Graph, must occurs in these two cases (if
and else if
), that means this algorithm will get all best-paths.
The precise of Dijkstra
Given a graph G G G, an total-order ≤ \leq ≤ on the set of the weight of all path ∣ A B ∣ |AB| ∣AB∣ (AB can be same) and a binary operator + + +, then the weight of a path (the sum of all edges on the path, denotes ∣ A B ∣ |AB| ∣AB∣) must satisfying a property (a bit like triangular inequality, but not same):
- If ∣ A B ∣ ≤ ∣ A C ∣ |AB| \leq |AC| ∣AB∣≤∣AC∣, then ∣ A B ∣ ≤ ∣ A C ∣ + ∣ C B ∣ |AB| \leq |AC| + |CB| ∣AB∣≤∣AC∣+∣CB∣ ( A B C ABC ABC maybe the same)
Then, Dijkstra is used to calculate the least-element under the order ≤ \leq ≤ of the weight ∣ S X ∣ |SX| ∣SX∣ where S S S is a start-point, X X X is a any point.
There are some examples:
-
A graph (may contains loop (self-loop or non-self-loop)) with all edges ≥ 0 \geq 0 ≥0, the weight of a path is the sum of all edges on it and given a start-point S S S, calculate the minimal-distance of ∣ S X ∣ |SX| ∣SX∣ (X is an arbitrary point)
Defining the Dijkstra-Order ≤ ( a , b ) = a ≤ b \leq(a,b) = a \leq b ≤(a,b)=a≤b, Dijkstra-Add + ( a , b ) = a + b +(a,b) = a + b +(a,b)=a+b
Then, we can found this satisfies the Dijkstra-Triangular-Inequality -
A graph (may contains loop (self-loop or non-self-loop)) with all edges ≤ 0 \leq 0 ≤0, the weight of a path is the sum of all edges on it and given a start-point S S S, calculate the maximal-distance of ∣ S X ∣ |SX| ∣SX∣ (X is an arbitrary point)
Defining the Dijkstra-Order ≤ ( a , b ) = a ≥ b \leq(a,b) = a \geq b ≤(a,b)=a≥b (notice here), Dijkstra-Add + ( a , b ) = a + b +(a,b) = a + b +(a,b)=a+b
Then, we can found this satisfies the Dijkstra-Triangular-Inequality -
A graph (may contains loop (self-loop or non-self-loop)) with all edges be any real-number in [ 0 , 1 ] [0, 1] [0,1], the weight of a path is the product of all edges on it and given a start-point S S S, calculate the maximal-distance of ∣ S X ∣ |SX| ∣SX∣ (X is an arbitrary point)
Defining the Dijkstra-Order ≤ ( a , b ) = a ≥ b \leq(a,b) = a \geq b ≤(a,b)=a≥b (notice here), Dijkstra-Add + ( a , b ) = a ∗ b +(a,b) = a * b +(a,b)=a∗b
Then, we can found this satisfies the Dijkstra-Triangular-Inequality
Floyd
Given a graph
G
G
G which is valid (e.g., no negative-loop, but maybe contains negative-edges, zero-loop)
Calculate the best-distance of
x
→
y
x \to y
x→y where
x
,
y
x, y
x,y are arbitrary points. (
D
i
s
t
[
x
]
[
x
]
Dist[x][x]
Dist[x][x] must be
0
0
0, due to
G
G
G is valid which implies no-negative-loop)
There must exists a best-path of all best-paths between any two points
x
,
y
x,y
x,y, in the form
x
→
.
.
.
→
y
x \to ... \to y
x→...→y where any two points on the path are distinct.
Let
m
m
m be the maximum of
.
.
.
...
..., the path will be
x
→
.
.
.
→
m
→
.
.
.
→
y
x \to ... \to m \to ... \to y
x→...→m→...→y where ...
are
<
m
< m
<m. It will be calculated by
D
p
[
x
]
[
m
]
+
D
p
[
m
]
[
y
]
Dp[ x][ m] + Dp[ m][ y]
Dp[x][m]+Dp[m][y]
We call such a best-path that maximized by
m
m
m.
.
So, all best-paths
a
→
b
a \to b
a→b has been grouped to several kinds:
.
1
Maximized by -1
, a -> ... -> b
where ...
<
=
−
1
<= -1
<=−1; this case will be calculated in the DP-loop-1.
.
2
Maximized by 0
, a -> ... -> b
where ...
<
=
0
<= 0
<=0; this case will be calculated in the DP-loop-2.
.
3
Maximized by 1
, a -> ... -> b
where ...
<
=
1
<= 1
<=1; this case will be calculated in the DP-loop-3.
.
4
Maximized by 2
, a -> ... -> b
where ...
<
=
2
<= 2
<=2; this case will be calculated in the DP-loop-4.
…
.
n+1
Maximized by n-1
, a -> ... -> b
where ...
<
=
(
n
−
1
)
<= (n-1)
<=(n−1); this case will be calculated in the DP-loop-(n+1).
Suppose all points are
[
0
,
n
)
[0, n)
[0,n):
Originally,
D
p
[
0
]
[
a
]
[
b
]
Dp[0][a][b]
Dp[0][a][b] denotes these paths
a
→
.
.
.
→
b
a \to ... \to b
a→...→b where
.
.
.
...
...
<
0
< 0
<0 (i.e.,
.
.
.
...
... is empty)
Firstly,
D
p
[
1
]
[
a
]
[
b
]
Dp[1][a][b]
Dp[1][a][b] denotes these paths
a
→
.
.
.
→
b
a \to ... \to b
a→...→b where
.
.
.
...
...
<
1
< 1
<1
Secondly,
D
p
[
2
]
[
a
]
[
b
]
Dp[2][a][b]
Dp[2][a][b] denotes these paths
a
→
.
.
.
→
b
a \to ... \to b
a→...→b where
.
.
.
...
...
<
2
< 2
<2
.
.
.
...
...
Nth,
D
p
[
n
]
[
a
]
[
b
]
Dp[n][a][b]
Dp[n][a][b] denotes these paths
a
→
.
.
.
→
b
a \to ... \to b
a→...→b where
.
.
.
...
...
<
n
< n
<n; it covers all possible paths, of course got the best-path.
Dp[ 0][ n][ n] = Edges[n][n];
for( mid = 0; mid < n; ++mid){
//< all best-paths that maximized by `mid` will be calculated here
for( l = 0; l < n; ++l){
for( r = 0; r < n; ++r){
Dp[ mid + 1][ l][ r] = min( self, Dp[ mid][ l][ mid] + Dp[ mid][ mid][ r]);
}
}
}
In fact, we can just use
D
p
[
n
]
[
n
]
Dp[n][n]
Dp[n][n] instead of
D
p
[
n
+
1
]
[
n
]
[
n
]
Dp[n + 1][n][n]
Dp[n+1][n][n] according to the definition.
That is,
D
p
[
i
]
[
a
]
[
b
]
=
m
i
n
(
s
e
l
f
,
D
p
[
i
−
1
]
[
a
]
[
i
−
1
]
+
D
p
[
i
−
1
]
[
i
−
1
]
[
b
]
)
Dp[i][a][b] = min( self, Dp[i-1][a][i - 1] + Dp[i-1][i - 1][b])
Dp[i][a][b]=min(self,Dp[i−1][a][i−1]+Dp[i−1][i−1][b]) where
D
p
[
i
−
1
]
[
x
x
]
Dp[i-1][xx]
Dp[i−1][xx] can be written as
[
i
]
[
x
x
]
[i][xx]
[i][xx] or
[
i
−
1
]
[
x
x
]
[i-1][xx]
[i−1][xx].
.
We need to prove that
D
p
[
i
−
1
]
[
i
−
1
]
[
y
]
Dp[i-1][i-1][y]
Dp[i−1][i−1][y] always equals to
D
p
[
i
]
[
i
−
1
]
[
y
]
Dp[i][i-1][y]
Dp[i][i−1][y] (also for
D
p
[
i
−
1
]
[
x
]
[
i
−
1
]
Dp[i-1][x][i-1]
Dp[i−1][x][i−1] and
D
p
[
i
]
[
x
]
[
i
−
1
]
Dp[i][x][i-1]
Dp[i][x][i−1]
.
Because the current DP is
m
i
d
=
i
mid = i
mid=i, all paths of
(
i
−
1
)
→
y
(i-1) \to y
(i−1)→y ((i-1) -> ... -> y
) are of the form where ...
must belong to
[
0
,
i
)
[0, i)
[0,i), moreover, the best-paths of
(
i
−
1
)
→
y
(i-1) \to y
(i−1)→y among these paths would not contain (i-1)
in ...
(according to the property of best-path).
.
Therefore, the best-paths from
(
i
−
1
)
→
y
(i-1) \to y
(i−1)→y of (all paths (i-1) -> ... -> y
where ...
belong to
[
0
,
i
−
1
)
[0, i-1)
[0,i−1)) and of (all paths (i-1) -> ... -> y
where ...
belong to
[
0
,
i
)
[0, i)
[0,i)) are same.
So, this is a special DP-Compactness where D p [ i − 1 ] [ y ] , D p [ x ] [ i − 1 ] Dp[i-1][y], Dp[x][i-1] Dp[i−1][y],Dp[x][i−1] maybe not visited (DP-Compactness), also maybe visited (its value would not be affected).
Floyd-Boolean
Given an non-equivalence-relation < < <, if we already have two relations a < b a<b a<b and c < d c<d c<d, now, given a new relation: a < c a < c a<c; according to Disjoint-Set, these 4 4 4 elements will be in the same set. However, we found that the relations of ( b , c ) ( b , d ) (b,c) (b,d) (b,c)(b,d) are not determined. So, < < < couldn’t be used in Disjoint-Set (it can be solved by Floyd-Boolean), only but for the equivalence-relation = = =.
Because
<
<
< has the property of Transitivity, that means if
x
R
y
x R y
xRy, it must could be divided into a path which consists of all edges (e.g.,
x
R
.
.
.
R
y
x R ... R y
xR...Ry) where all points are distinct.
According to Floyd-Algorithm, we use
b
o
o
l
E
d
g
e
s
[
a
]
[
b
]
bool Edges[ a][ b]
boolEdges[a][b] denotes whether
a
R
b
a R b
aRb or not;
Let
m
m
m be the maximal-element in
.
.
.
...
..., then
x
R
.
.
.
R
m
R
.
.
.
R
y
x R ... R m R ... Ry
xR...RmR...Ry, at the Loop-(m) in Floyd-DP , we already got the answer of
[
x
]
[
m
]
[x][m]
[x][m] and
[
m
]
[
y
]
[m][y]
[m][y] (due to the ...
in
x
R
.
.
.
R
m
xR...Rm
xR...Rm and
m
R
.
.
.
R
y
mR...Ry
mR...Ry are all
<
m
< m
<m), so we can update:
[
x
]
[
y
]
∣
=
(
[
x
]
[
m
]
&
&
[
m
]
[
y
]
)
[x][y] \ |= ([x][m] \&\& [m][y])
[x][y] ∣=([x][m]&&[m][y])
for( int l, r, mid = 0; mid < _range; ++mid){
for( l = 0; l < _range; ++l){
if( false == _edges[ l][ mid]){
continue;
}
for( r = 0; r < _range; ++r){
_edges[ l][ r] |= _edges[ mid][ r];
}
}
}
+
Whether
(
x
,
x
)
(x,x)
(x,x) should be always true of false, depending on different context;
[
x
]
[
x
]
=
t
r
u
e
[x][x] = true
[x][x]=true means that
x
x
x is related on itself;
.
This differs from the traditional Floyd
D
i
s
t
[
x
]
[
x
]
Dist[x][x]
Dist[x][x] which always be
0
0
0 independent from
e
d
g
e
s
edges
edges.
There is a variety based on the above, as the
E
d
g
e
s
[
]
[
]
Edges[][]
Edges[][] is not given as a whole at beginning, rather dynamic. That is, first-step given a relation and second-step given a relation,
.
.
.
...
.... Simultaneously, for each step
i
i
i, you should maintain the relation-map under the previous
i
i
i steps;
The brute device is
m
∗
O
(
n
3
)
m * O(n^3)
m∗O(n3); there is a device that is
m
∗
O
(
n
2
)
m * O(n^2)
m∗O(n2).
Suppose that we already got the relation-map
G
G
G under the previous
i
−
1
i-1
i−1 relations. Now, we have a new relation
(
x
,
y
)
(x,y)
(x,y) in
i
i
i step, what would it affects in
G
G
G?
1
(
x
,
y
)
=
t
r
u
e
(x,y) = true
(x,y)=true
2
(
l
,
y
)
(l, y)
(l,y) will be true if
(
l
,
x
)
=
t
r
u
e
(l, x) = true
(l,x)=true
3
(
x
,
r
)
(x, r)
(x,r) will be true if
(
y
,
r
)
=
t
r
u
e
(y, r) = true
(y,r)=true
4
(
l
,
r
)
(l,r)
(l,r) will be true if
(
l
,
x
)
=
(
y
,
r
)
=
t
r
u
e
(l,x) = (y,r) = true
(l,x)=(y,r)=true
void Add_relation( int _from, int _to){
if( ptr_relation[ _from][ _to]){ //< vital
return;
}
ptr_relation[ _from][ _to] = true;
for( int l = 0; l < range; ++l){
ptr_relation[ l][ _to] |= ptr_relation[ l][ _from];
}
for( int r = 0; r < range; ++r){
ptr_relation[ _from][ r] |= ptr_relation[ _to][ r];
}
for( int r, l = 0; l < range; ++l){
for( r = 0; r < range; ++r){
ptr_relation[ l][ r] |= (ptr_relation[ l][ _from] && ptr_relation[ _to][ r]);
}
}
}
Least-Loop in Undirected-Graph
When handling with Undirected-Graph, it is vital to transfer it equivalently to Directed-Graph, because either Linked-List or Adjacency-Matrix always storing graph as Directed-Graph.
Because the original undirected-graph
G
G
G has been replaced to directed-graph
G
1
G1
G1, we need to prove that The least-loop (consists of
≥
3
\geq 3
≥3 distinct points) in
G
1
G1
G1 is also be a valid-least-loop in
G
G
G, vice versa
+
a loop in
G
G
G a-b-c-a
, is also a loop in
G
1
G1
G1 a->b->c->a
or a<-b<-c<-a
+
the least-loop in
G
1
G1
G1 (all points on the loop are distinct) is also the least-loop in
G
G
G by replacing all directed-edges with its corresponding undirected-edges.
For example, the answer directed-loop is a -> b -> c -> d -> a
A wrong device
Because all points are distinct, and the points count
≥
3
\geq 3
≥3, we can divide a loop into two parts:
1
a -> b
E
d
g
e
[
a
]
[
b
]
Edge[a][b]
Edge[a][b]
2
b -> c -> d -> a
define
D
i
s
t
a
n
c
e
[
b
]
[
a
]
Distance[b][a]
Distance[b][a] as the least-distance from b
to a
must pass though at least one middle point (i.e., b -> x -> a
), and satisfying all points in the path are distinct.
The definition is correct to calculate the least-loop, but D i s t a n c e Distance Distance would not be achieved by Floyd.
If your code is:
for( mid = 0; mid < n; ++mid){
for( l = 0; l < n; ++l){
for( r = 0; r < n; ++r){
auto lef = min( edge[ l][ mid], dist[ l][ mid]);
auto rig = min( edge[ mid][ r], dist[ mid][ r]);
dist[ l][ r] = min( lef + rig, self);
}
}
}
When you update
D
i
s
t
[
a
]
[
m
i
d
]
+
D
i
s
t
[
m
i
d
]
[
b
]
Dist[ a][ mid] + Dist[ mid][ b]
Dist[a][mid]+Dist[mid][b] to
D
i
s
t
[
a
]
[
b
]
Dist[ a][ b]
Dist[a][b], that is a ...pre... mid ...suf... b
where mid
is the maximal point among ...pre...
and ...suf...
except a, b
;
Although ...pre...
are distinct, you can not guarantee that a ...pre...
is distinct with ...suf... b
(e.g., a (b) mid (a) b
)
The essential problem is that the definition of
D
i
s
t
a
n
c
e
Distance
Distance differs from that in Floyd.
We know that the distance (also the least-path) in a valid-graph always consisting of distinct points, but once you define the
D
i
s
t
a
n
c
e
Distance
Distance as above, e.g.,
D
i
s
t
a
n
c
e
[
a
]
[
b
]
Distance[a][b]
Distance[a][b] in Floyd equals to
E
d
g
e
s
[
a
]
[
b
]
Edges[ a][ b]
Edges[a][b], while this is invalid now, it need a middle point m
, therefore the case a -> m -> a -> b
is possible to update
D
i
s
t
a
n
c
e
Distance
Distance.
The correct device is, we divide the loop by points (not by edges as above); a loop ... -> a -> b -> c -> ...
where b
is the maximal point; we divide by abc
1
a -> b -> c
, corresponds to two edges
2
c -> ... -> a
, equals Distance[c][a]
at the Floyd-Loop(b)-Before; (but at Floyd-Loop(b)-After, Distance[c][a]
may equals c -> ... -> a
where ...
may contains b
;
The length of loop is easy to handle; the path is more complicated.
Because c -> ... -> a
in the loop must not contains b
, while this is satisfied just at Floyd-Loop(b)-Before; The approach is just like the update as the length, the path also be updated (replaced) once the length has be updated.
This idea is very important, it would not cost too much though we replace the whole path and find a new whole path, as the path at most contains
n
=
100
n = 100
n=100 points;
Do not pursue a device that must calculate the answer path just once; Always keep your mind open, if the time-cost is reasonable.
Tips: The answer-loop may be not derived from b
(the maximal point), just ensure ... < b
is enough, a,c
can greater than b
;
e.g., 1 -> 3 -> 5 -> 2
, we choose 1 -> 3 -> 5
as division (3
is not the maximum), due to 2 < 3
, also use edge[1][3], edge[3][5]
and distance[5][1]
(at Loop-(3)-Before), we can also get the answer; not necessary at 3 -> 5 -> 2
;
Code
vector< int> Path;
int Maximal_point[ 102][ 102];
void Dfs( int _l, int _r){
if( -1 == Maximal_point[ _l][ _r]){
return;
}
auto mid = Maximal_point[ _l][ _r];
Dfs( _l, mid);
Path.push_back( mid);
Dfs( mid, _r);
}
int Edges[ 102][ 102];
int Dist[ 102][ 102];
void __Solve( int _test_id){
( void)_test_id;
//--
int n, m;
scanf("%d%d", &n, &m);
memset( Edges, 0x7F, sizeof( Edges));
for( int i = 0; i < m; ++i){
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
-- a, -- b;
Edges[ a][ b] = min( w, Edges[ a][ b]);
Edges[ b][ a] = Edges[ a][ b];
}
for( int j, i = 0; i < n; ++i){
for( j = 0; j < n; ++j){
Dist[ i][ j] = Edges[ i][ j];
}
}
for( int i = 0; i < n; ++i){
Dist[ i][ i] = 0;
}
memset( Maximal_point, -1, sizeof( Maximal_point));
int loop_length = 0x7F7F7F7F;
for( int start, end, mid = 0; mid < n; ++mid){
for( int l = 0; l < n; ++l){
for( int r = 0; r < n; ++r){
if( (l != r) && (l != mid) && (r != mid)
&& (0x7F7F7F7F != Edges[ l][ mid])
&& (0x7F7F7F7F != Edges[ mid][ r])
&& (0x7F7F7F7F != Dist[ l][ r])){
if( Edges[ l][ mid] + Edges[ mid][ r] + Dist[ l][ r] < loop_length){
loop_length = Edges[ l][ mid] + Edges[ mid][ r] + Dist[ l][ r];
Path.clear();
Path.push_back( r);
Path.push_back( mid);
Path.push_back( l);
Dfs( l, r);
}
}
}
}
//--
for( start = 0; start < n; ++start){
if( 0x7F7F7F7F == Dist[ start][ mid]){
continue;
}
for( end = 0; end < n; ++end){
if( 0x7F7F7F7F == Dist[ mid][ end]){
continue;
}
if( ( 0x7F7F7F7F == Dist[ start][ end])
|| (Dist[ start][ mid] + Dist[ mid][ end] < Dist[ start][ end])){
Dist[ start][ end] = Dist[ start][ mid] + Dist[ mid][ end];
Maximal_point[ start][ end] = mid;
}
}
}
}
if( Path.empty()){
cout << "No solution.";
return;
}
for( auto i : Path){
cout << i + 1 << ' ';
}
}
Pseudo-Floyd
Given a directed-graph, calculate the least-path a → b a \to b a→b which consists of exact k k k edges.
This is solvable even if there exists negative-loop, because the count of paths is limited once the length of path is definite.
If you use Shortest-Path devices (e.g., Dijkstra) by performing k k k steps, you will get the least-path a → b a \to b a→b which consists of at most k k k edges (not exactly equal to k k k edges)
The correct device is Pseudo-Floyd which is a variety of Floyd, but more simpler.
Let
D
1
[
a
]
[
b
]
D1[a][b]
D1[a][b] denotes the least-path amongst all paths
a
→
b
a \to b
a→b that consists of
l
1
l1
l1 edges,
D
2
[
a
]
[
b
]
D2[a][b]
D2[a][b] denotes the same as
D
1
D1
D1 but consists of
l
2
l2
l2 edges.
We will calculate
D
i
s
t
[
]
[
]
Dist[][]
Dist[][] by using
D
1
,
D
2
D1, D2
D1,D2, which denotes the least-path consists of
l
1
+
l
2
l1 + l2
l1+l2 edges.
.
Once this is realizable (it has the property of addition), we can
1
+
1
+
1
+
.
.
.
=
k
1 + 1 + 1 + ... = k
1+1+1+...=k to obtain the answer (furthermore,
1
+
2
+
4
+
.
.
.
=
k
1 + 2 + 4 + ... = k
1+2+4+...=k using the bit-factorization)
//> the order of `l, r, mid` can be arbitrary
for( l = 0; l < n; ++l){
for( r = 0; r < n; ++r){
for( mid = 0; mid < n; ++mid){
//> `D1 -> D2`
lef = D1[ l][ mid], rig = D2[ mid][ r];
Dist[ l][ r] = min( lef + rig, self);
//> `D2 -> D1`
lef = D2[ l][ mid], rig = D1[ mid][ r];
Dist[ l][ r] = min( lef + rig, self);
}
}
}
Proof for its validity:
We have
D
1
D1
D1 denotes all paths consists of
l
1
l1
l1 edges, also the same for
D
2
D2
D2.
For any path
a
→
b
a \to b
a→b consists of
l
1
+
l
2
l1 + l2
l1+l2 edges, there must exist a point
m
m
m such that
a
→
.
.
.
→
m
→
.
.
.
→
b
a \to ... \to m \to ... \to b
a→...→m→...→b where
a
.
.
.
m
a ... m
a...m consists of
l
1
l1
l1 edges and
m
.
.
.
b
m ... b
m...b consists of
l
2
l2
l2 edges. That is,
D
1
[
a
]
[
m
]
D1[a][m]
D1[a][m] and
D
2
[
m
]
[
b
]
D2[m][b]
D2[m][b] would forms this type path. Therefore, just iterate all
m
m
m for a specific
l
,
r
l, r
l,r then we will get all
a
.
.
.
b
a ... b
a...b consists of
l
1
+
l
2
l1 + l2
l1+l2 edges.
Annotation for this device:
+
The order of l, r, mid
is arbitrary which differs from the unique order mid -> l -> r
in Floyd.
.
The definition of
D
i
s
t
[
]
[
]
Dist[][]
Dist[][] between Floyd and Pseudo-Floyd are different; the former means the paths
a
.
.
.
b
a ... b
a...b where
.
.
.
...
... are all
≤
i
\leq i
≤i (
i
i
i is the current-DP-loop), so the
.
.
.
...
... of
a
.
.
.
i
a...i
a...i and
i
.
.
.
b
i...b
i...b should
<
i
< i
<i (the DP transformation), the definition is changing in different loop; while the definition in the latter is fixed;
+
The update for
D
i
s
t
Dist
Dist are different.
.
In Floyd, it is Dist[a][b] = min( Dist[l][mid] + Dist[mid][r], self)
; here, it becomes Dist[a][b] = min( D1[l][mid] + D2[mid][r], self)
. This is the kernel difference between these two devices. You could understand it from the view of the definition of
D
i
s
t
Dist
Dist.
+
Actually, the case of //> D2 -> D1
can be ignored.
.
It has already being contained in the case D1 -> D2
(in other words, choosing any one of this two cases is enough.) For a path
a
.
.
.
m
.
.
.
b
a ... m ... b
a...m...b where
a
.
.
.
m
a...m
a...m consists of
l
2
l2
l2, then there also exists a point
n
n
n such that
a
.
.
.
n
.
.
.
b
a ... n ... b
a...n...b where
a
.
.
.
n
a...n
a...n consists of
l
1
l1
l1;
Firstly hashing all points to
[
0
,
100
]
[0, 100]
[0,100]; every Pseudo-Floyd process costs
1
e
6
1e6
1e6.
For the length
k
k
k, we divide it by bit-factorization
1
,
2
,
4
,
.
.
.
1, 2, 4, ...
1,2,4,... and use Quick-Power to sum them up. (the total cost is
20
∗
1
e
6
20 * 1e6
20∗1e6)
We use
A
n
s
Ans
Ans to denotes the answer-distance, initially it denotes the path consists of
0
0
0 edges, then
0
+
(
k
)
=
k
0 + (k) = k
0+(k)=k finally we got the answer. Therefore, only
A
n
s
[
i
]
[
i
]
=
0
Ans[i][i] = 0
Ans[i][i]=0 and others be invalid.
We use
D
i
s
t
Dist
Dist to denotes the path consists of
i
i
i where
i
i
i is power to
2
2
2, according to the process of Quick-Power, initially
D
i
s
t
[
a
]
[
b
]
Dist[a][b]
Dist[a][b] should represents the paths consists of
1
1
1 edges. In other words, it corresponds to all edges inputted by question. Do not set
D
i
s
t
[
i
]
[
i
]
=
0
Dist[i][i] = 0
Dist[i][i]=0 manually, because it is a path consists of
1
1
1 edges.
+
the Function above void Merge( int (* _a)[ 102], int (* _b)[ 102], int (* _ans)[ 102]){
.
Do not operate on _ans
directly, this is a very fallible point; because _ans
may equals to _a, _b
, once you modify _ans
, the meaning of _a
is also changed, then _ans = min( _a + _b, self)
is erroneous. You must use another temp
to perform the process, then finally assign it to _ans
;
int K, N, T, S, E;
int Dist_fixed[ 102][ 102];
int Ans[ 102][ 102];
void Merge( const int (* _a)[ 102], const int (* _b)[ 102], int (* _ans)[ 102]){
static int temp[ 102][ 102]; //< `_ans` may identify with `_a, _b`
memset( temp, 0x7F, sizeof( temp));
for( int mid, r, l = 0; l < N; ++ l){
for( r = 0; r < N; ++r){
for( mid = 0; mid < N; ++mid){
//> `_a` -> `_b`
auto lef = _a[ l][ mid], rig = _b[ mid][ r];
if( (0x7F7F7F7F != lef) && (0x7F7F7F7F != rig)){
temp[ l][ r] = min( lef + rig, temp[ l][ r]);
}
// //> `_b` -> `_a`
// lef = _b[ l][ mid], rig = _a[ mid][ r];
// if( (0x7F7F7F7F != lef) && (0x7F7F7F7F != rig)){
// temp[ l][ r] = min( lef + rig, temp[ l][ r]);
// }
}
}
}
memcpy( _ans, temp, sizeof( temp));
}
//----
void __Solve( int _test_id){
( void)_test_id;
//--
Hash_mapping< int> hhash;
scanf("%d%d%d%d", &K, &T, &S, &E);
hhash.Add( S); S = hhash.Get_hash( S);
hhash.Add( E); E = hhash.Get_hash( E);
memset( Dist_fixed, 0x7F, sizeof( Dist_fixed));
for( int i = 0; i < T; ++i){
int w, a, b;
scanf("%d%d%d", &w, &a, &b);
hhash.Add( a); a = hhash.Get_hash( a);
hhash.Add( b); b = hhash.Get_hash( b);
Dist_fixed[ a][ b] = min( w, Dist_fixed[ a][ b]);
Dist_fixed[ b][ a] = Dist_fixed[ a][ b];
}
N = hhash.Get_size();
memset( Ans, 0x7F, sizeof( Ans));
for( int i = 0; i < N; ++i){
Ans[ i][ i] = 0;
}
//* Now, `Dist_fixed` denotes pass though `1` edges, `Ans` pass though `0` edges.
while( K > 0){
if( K & 1){
Merge( Ans, Dist_fixed, Ans);
}
K >>= 1;
Merge( Dist_fixed, Dist_fixed, Dist_fixed);
}
cout << Ans[ S][ E];
}