題解/算法 {342. 道路与航线}

link

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 w0 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 wN (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 bx and y → a y \to a ya.)

Given a start-point S. calculate the shortest-distance of S → x S \to x Sx 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 SS1
+ 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 S1S2 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) (ad)(be), so S 1 → S 2 S1 \to S2 S1S2 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, aS1, b ∈ S 2 / S 3 / . . b \in S2/S3/.. bS2/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,...,Si1 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 aSi, 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]+(ca), 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[ba] 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[ba] 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 cd, 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]);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值