• `Algorithm-Solution` `AcWing` 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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]);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
  • 相关阅读:
    参数传递方式
    大家都能看得懂的源码之 ahooks useVirtualList 封装虚拟滚动列表
    接口测试——接口协议抓包分析与mock_L2
    继GitHub的Copilot收费后,亚马逊推出了 CodeWhisperer,感觉不错哟!
    调用CFCA金信反欺诈服务相关接口,很详细
    Responder
    C++实测无锁队列concurrentqueue、boost.spinlock 和 std::mutex 在多线程情况下的性能表现
    Remote & Local File Inclusion (RFI/LFI)-文件包含漏洞
    Ajax跨域请求的两种实现方式
    WEB开发技能树-PHP-用户登录
  • 原文地址:https://blog.csdn.net/qq_66485519/article/details/128162001