Solution
Given a graph
G
G
G which contains two types of edges:
+
E
1
(
a
,
b
,
w
)
E1( a, b, w)
E1(a,b,w) means there is a undirected edge with a weight
w
≥
0
w \geq 0
w≥0 between
a
,
b
a, b
a,b.
+
E
2
(
a
,
b
,
w
E2( a, b, w
E2(a,b,w) means a directed edge from
a
a
a to
b
b
b with a weight
w
∈
N
w \in N
w∈N (maybe negative)
And a property on E 2 ( a , b , w ) E2( a, b, w) E2(a,b,w): there is no path (consists of E 1 , E 2 E1, E2 E1,E2) that from b b b to a a a. Property-1
This property is pivotal, it shows that
G
G
G is a special kind of DAG.
+
Firstly, we divide
G
G
G into several set according to
E
1
E1
E1, let it be
S
1
,
S
2
,
S
3
,
.
.
.
S1, S2, S3, ...
S1,S2,S3,... (any two points are accessible for each other in a set, but any two points are not connected if they are not in a same set).
If we suppose
S
1
,
.
.
.
S1, ...
S1,... are also points, then you will get a graph
G
S
GS
GS which has no edge.
+
Secondly, for a edge
E
2
(
a
,
b
,
w
)
E2( a, b, w)
E2(a,b,w) then we connect a directed edge from
a
a
a to
b
b
b, so now,
G
S
GS
GS becomes a DAG (according to Property-1) (although the direct information by the question is, if
E
2
(
a
,
b
)
E2( a, b)
E2(a,b) then no path from
b
b
b to
a
a
a, it also indicates that there is no path from
x
x
x to
y
y
y where
b
→
x
b \to x
b→x and
y
→
a
y \to a
y→a.)
Given a start-point S. calculate the shortest-distance of S → x S \to x S→x where x x x is any point of G G G.
The answer is unique (either no-path from
S
S
S to
x
x
x, or there exists a shortest-distance) although there exist negative-edge in
G
G
G.
But, because all negative-edges form a DAG, so in fact, there is no negative-loop in
G
G
G.
|----------------|
| v
S5------>S1----->S2------>S4
| ^
|------>S3-------|
S6
Every set S i Si Si is a undirected, non-negative-weighted, connected graph
Suppose
S
∈
S
1
S \in S1
S∈S1
+
For any set that is not accessible for
S
1
S1
S1, any point in such sets is No-Path for
S
S
S.
+
For any point in
S
1
S1
S1, it can be solved by Dijkstra only acted in
S
1
S1
S1.
+
For these accessible sets
S
2
,
S
3
,
.
.
.
S2, S3, ...
S2,S3,..., they are all solvable.
...
(there is one thing to clarify,
S
1
→
S
2
S1 \to S2
S1→S2 not means a edge between two sets, e.g.
S
1
=
(
a
,
b
,
S
)
,
S
2
=
(
d
,
e
,
f
)
S1 = (a,b,S), S2 = (d,e,f)
S1=(a,b,S),S2=(d,e,f) and edges
(
a
−
d
)
(
b
−
e
)
(a-d) (b-e)
(a−d)(b−e), so
S
1
→
S
2
S1 \to S2
S1→S2 actually denotes a set of edges with origin-point in
S
1
S1
S1 and destiny-point in
S
2
S2
S2)
We get its Topological-Sequence (S1, S2, S3, S4)
++
Firstly iterate to
S
1
S1
S1 according to the Topo-Sequence, after the operation
D
i
j
k
(
S
1
)
Dijk( S1)
Dijk(S1), now we have
S
1
S1
S1 is correct (that is, the answer for any point in
S
1
S1
S1 is determined), then use
S
1
S1
S1 and all adjacent-edges to update the outside-points, more precise, (for any edge
(
a
,
b
,
w
)
(a,b,w)
(a,b,w) in
E
2
E2
E2 where
a
∈
S
1
,
a \in S1,
a∈S1,
b
∈
S
2
/
S
3
/
.
.
b \in S2/S3/..
b∈S2/S3/.., perform
D
i
s
t
[
b
]
=
m
i
n
(
s
e
l
f
,
D
i
s
t
[
a
]
+
w
)
Dist[ b] = min( self, Dist[a] + w)
Dist[b]=min(self,Dist[a]+w), notice that
D
i
s
t
[
a
]
Dist[a]
Dist[a] is correct at present)
++
Secondly iterate to
S
2
S2
S2 according to the Topo-Sequence, after the operation
D
i
j
k
(
S
2
)
Dijk( S2)
Dijk(S2) (here it differs from
D
i
j
k
(
S
1
)
Dijk( S1)
Dijk(S1) which is the standard-Dijkstra-from that has a start-point
S
S
S with
D
i
s
t
[
S
]
=
0
,
D
i
s
t
[
o
t
h
e
r
s
]
=
I
n
f
i
n
i
t
y
Dist[S] = 0, Dist[others] = Infinity
Dist[S]=0,Dist[others]=Infinity, but the start-point in here is all points in
S
2
S2
S2 whose
D
i
s
t
≠
I
n
f
i
n
i
t
y
Dist \neq Infinity
Dist=Infinity, i.e., it has multiple start-points), we need to prove that
S
2
S2
S2 is correct now after
D
i
j
k
(
S
2
)
Dijk( S2)
Dijk(S2) (once we proved it, then all
S
i
Si
Si are correct)
More generally, the Topo-Sequence
S
1
,
S
2
,
.
.
.
,
S
i
S1, S2, ..., Si
S1,S2,...,Si (all
S
1
,
.
.
.
,
S
i
−
1
S1, ..., S_{i-1}
S1,...,Si−1 are correct), prove
S
i
Si
Si is correct after
D
i
j
k
(
S
i
)
Dijk( Si)
Dijk(Si)
For any point
a
∈
S
i
a \in Si
a∈Si, the shortest-path of
a
a
a is of the form
.
.
.
,
a
..., a
...,a (
.
.
.
...
... must belongs to
S
1
/
.
.
.
/
S
i
S1/.../Si
S1/.../Si), let
b
b
b be the point that satisfies
c
1
,
b
,
c
2
,
a
c1, b, c2, a
c1,b,c2,a where
c
1
c1
c1 are all not belong to
S
i
Si
Si and
b
,
c
2
,
a
b, c2, a
b,c2,a are all belong to
S
i
Si
Si.
(1)
If
b
=
a
b = a
b=a, that is
.
.
.
,
c
,
a
..., c, a
...,c,a (suppose
c
c
c belongs to
S
j
Sj
Sj),
D
i
s
t
[
a
]
=
D
i
s
t
[
c
]
+
(
c
−
a
)
Dist[ a] = Dist[ c] + (c-a)
Dist[a]=Dist[c]+(c−a), it will be performed in
S
j
Sj
Sj with
D
i
s
t
[
c
]
Dist[c]
Dist[c] is correct. So
D
i
s
t
[
a
]
Dist[a]
Dist[a] is already correct even if before the performance of
D
i
j
k
(
S
i
)
Dijk( Si)
Dijk(Si).
(2)
if
b
≠
a
b \neq a
b=a, that is
.
.
.
,
c
,
b
,
.
.
.
,
a
..., c, b, ..., a
...,c,b,...,a, now
D
i
s
t
[
b
]
Dist[ b]
Dist[b] is correct (according to the above-(1)),
b
b
b is a start-point of
D
i
j
k
(
S
i
)
Dijk( Si)
Dijk(Si) and all edges in
b
,
.
.
.
,
a
b, ..., a
b,...,a are non-negative, (
b
,
.
.
.
,
a
b, ..., a
b,...,a must be the shortest-path in
S
i
Si
Si)
Although
D
i
s
t
[
a
]
=
D
i
s
t
[
b
]
+
D
i
s
t
[
b
→
a
]
Dist[a] = Dist[ b] + Dist[ b \to a]
Dist[a]=Dist[b]+Dist[b→a] where
D
i
s
t
[
b
]
Dist[b]
Dist[b] maybe negative,
D
i
j
k
(
S
i
)
Dijk( Si)
Dijk(Si) just focuses on
S
i
Si
Si where all edges are non-negative in the graph, in the other words,
D
i
j
k
(
S
i
)
Dijk( Si)
Dijk(Si) is used to solve
D
i
s
t
[
b
→
a
]
Dist[ b \to a]
Dist[b→a] which is non-negative (not involves the negative
D
i
s
t
[
b
]
Dist[ b]
Dist[b], because
D
i
s
t
[
b
]
Dist[b]
Dist[b] is correct, it will be viewed as a start-point which be putted into the heap of Dijkstra originally; it’s important to realize that, the distance of start-point can be any number not necessary be
0
0
0 as usual, so as provide that all edges are non-negative in the graph, Dijkstra is also valid; e.g., if
D
i
s
t
[
s
]
=
0
Dist[ s] = 0
Dist[s]=0 then you will get
D
i
s
t
[
e
]
=
x
Dist[ e] = x
Dist[e]=x after Dijkstra, you will get
D
i
s
t
[
e
]
=
x
+
y
Dist[ e] = x + y
Dist[e]=x+y if you set
D
i
s
t
[
s
]
=
y
Dist[ s] = y
Dist[s]=y)
...
One another thing is that, not all start-points (whose
D
i
s
t
[
]
≠
I
n
f
i
n
i
t
y
Dist[] \neq Infinity
Dist[]=Infinity) are correct, e.g., a start-point whose shortest-path is the case-(2) as discussed above.
But
s
s
s must be correct, if
s
s
s has the least-
D
i
s
t
Dist
Dist among other start-points, that is, after we put all start-points into the heap, its top-element must be correct. Maybe the second-top-element is also correct (i.e., its
D
i
s
t
Dist
Dist will never be updated in the process of
D
i
j
k
Dijk
Dijk), also maybe not.
After
D
i
j
k
(
S
i
)
Dijk( Si)
Dijk(Si),
S
i
Si
Si is correct; next step, we will update
S
j
Sj
Sj using
E
2
E2
E2 (we called this process Update-Next).
In fact, Update-Next can be done in the Dijk-process; because when a element
c
c
c firstly out of heap, its
D
i
s
t
Dist
Dist is correct, so when it updates successfully
D
i
s
t
Dist
Dist in Dijkstra
c
→
d
c \to d
c→d, we check if
d
d
d is also in
S
i
Si
Si then put it into heap as the usual process that Dijkstra do, otherwise
d
d
d belongs to a another
S
j
Sj
Sj, then do not put it into heap, just update
d
d
d is enough which means the process Update-Next.
Code
class Dijkstra{
void Initialize( int _start){
memset( ptr_distance, 0x7F, sizeof( _Distance_Type) * ptrRef_graph->Get_pointsCount());
memset( ptr_isFirstTime, true, sizeof( bool) * ptrRef_graph->Get_pointsCount());
ptr_distance[ _start] = 0;
}
void Work( int _set_id){
while( false == heap.empty()){
heap.pop();
}
//--
for( auto i : Set[ _set_id]){
if( ptr_distance[ i] != 0x7F7F7F7F){
heap.push( make_pair( ptr_distance[ i], i));
}
}
int cur, nex, edge;
while( false == heap.empty()){
cur = heap.top().second;
heap.pop();
if( false == ptr_isFirstTime[ cur]){
continue;
}
ptr_isFirstTime[ cur] = false;
for( edge = ptrRef_graph->Head[ cur]; ~edge; edge = ptrRef_graph->Next[ edge]){
nex = ptrRef_graph->Vertex[ edge];
if( ptr_distance[ cur] + ptrRef_graph->Weight[ edge] < ptr_distance[ nex]){
ptr_distance[ nex] = ptr_distance[ cur] + ptrRef_graph->Weight[ edge];
if( Set_id[ nex] == Set_id[ cur]){
heap.push( make_pair( ptr_distance[ nex], nex));
}
}
}
}
}
};
void __Solve( int _test_id){
( void)_test_id;
//--
int T, R, P, S;
scanf("%d%d%d%d", &T, &R, &P, &S);
-- S;
Graph_Weighted< int> g( T, 2 * R + P);
g.Initialize( T);
DisjointSet d( T);
d.Initialize( T);
memset( Set_id, -1, sizeof( Set_id));
int set_count = 0;
for( int i = 0; i < R; ++i){
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
-- a, -- b;
g.Add_edge( a, b, w);
g.Add_edge( b, a, w);
d.Merge( a, b);
}
for( int i = 0; i < T; ++i){
if( d.Is_root( i)){
Set_id[ i] = set_count;
++ set_count;
}
}
for( int i = 0; i < T; ++i){
Set_id[ i] = Set_id[ d.Get_root( i)];
}
for( int i = 0; i < T; ++i){
if( Set_id[ i] == -1){
Set_id[ i] = set_count;
++ set_count;
}
}
for( int i = 0; i < T; ++i){
Set[ Set_id[ i]].push_back( i);
}
Graph_UnWeighted set_g( set_count, P);
set_g.Initialize( set_count);
for( int i = 0; i < P; ++i){
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
-- a, -- b;
g.Add_edge( a, b, w);
ASSERT_( Set_id[ a] != Set_id[ b]);
set_g.Add_edge( Set_id[ a], Set_id[ b]);
}
Topological_Sequence t( &set_g);
bool ttt = t.Work();
ASSERT_( ttt);
Dijkstra< int, int> dijk( &g);
dijk.Initialize( S);
for( int i = 0; i < set_count; ++i){
dijk.Work( t.Sequence[ i]);
}
for( int i = 0; i < T; ++i){
if( dijk.Distance[ i] == 0x7F7F7F7F){
printf("NO PATH\n");
}
else{
printf("%d\n", dijk.Distance[ i]);
}
}
}