【POJ】3463 Sightseeing 最短路+比最短路大一的路(最短路 or 最短路+DP)
阿新 • • 發佈:2019-01-07
BAPC 2006 Qualification
題型:求最短路以及比最短路長度大一的次短路,並要求計數。
傳送門:【POJ】3463 Sightseeing
題目大意:給你n個點m條邊的有向圖(unidirectional是有向!),讓你求最短路以及長度比最短路大一的路的數量。題目保證數量不超過10^9。(2 ≤ N ≤ 1,000 and 1 ≤ M ≤ 10,000)
題目分析:
如果直接在Dijkstra演算法中求最短路的數量以及次短路的數量的話,那麼演算法中就有四種情況存在:
1.比最短路短
2.和最短路一樣短
3.比次短路短
4.和次短路一樣長
都要考慮到,且要注意細節。
每次可以在最短路上增廣或者次短路上增廣,最短路上增廣包括所有四種情況,次短路增廣包括後兩種情況(次短路得到的路肯定不可能比最短路短),那麼判斷是否標號永久化就需要對同一個結點永久化兩次,一次是最短路,一次是次短路。
程式碼如下:
然後是另一種方法:
Dijkstra依舊求出最短路的數量,然後用DP在反圖上倒著記憶化搜尋,如果d[u] == d[v] + w[v,u],說明到目前為止還在最短路上,那麼可以在繼續遞迴中得到次短路的數量cnt1,則屬於該結點的次短路數量cnt += cnt1。如果d[u] == d[v] + w[v,u] - 1,說明正在次短路的一條邊上,後面一定都是最短路的邊了,則下面直接得到最短路數量cnt2,則次短路數量cnt += cnt2。感覺比上面的方法還要簡單~
程式碼如下:
題型:求最短路以及比最短路長度大一的次短路,並要求計數。
傳送門:【POJ】3463 Sightseeing
題目大意:給你n個點m條邊的有向圖(unidirectional是有向!),讓你求最短路以及長度比最短路大一的路的數量。題目保證數量不超過10^9。(2 ≤ N ≤ 1,000 and 1 ≤ M ≤ 10,000)
題目分析:
如果直接在Dijkstra演算法中求最短路的數量以及次短路的數量的話,那麼演算法中就有四種情況存在:
1.比最短路短
2.和最短路一樣短
3.比次短路短
4.和次短路一樣長
都要考慮到,且要注意細節。
每次可以在最短路上增廣或者次短路上增廣,最短路上增廣包括所有四種情況,次短路增廣包括後兩種情況(次短路得到的路肯定不可能比最短路短),那麼判斷是否標號永久化就需要對同一個結點永久化兩次,一次是最短路,一次是次短路。
程式碼如下:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std ; #define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i ) #define REP( i , n ) for ( int i = 0 ; i < n ; ++ i ) #define clear( A , X ) memset ( A , X , sizeof A ) typedef int type_d ; const int maxN = 1005 ; const int maxQ = 100005 ; const int maxH = 100005 ; const int maxE = 100005 ; const int oo = 0x3f3f3f3f ; struct Heap { int num ; int kind ; type_d d ; Heap () {} Heap ( type_d D , int Num , int Kind ) : d(D) , num(Num) , kind(Kind) {} } ; struct Edge { int v , n , w ; Edge () {} Edge ( int V , int W , int N ) : v(V) , w(W) , n(N) {} } ; struct Priority_Queue { Heap heap[maxH] ; int top ; void swap ( Heap &a , Heap &b ) { Heap tmp ; tmp = a ; a = b ; b = tmp ; } int cmp ( const Heap a , const Heap b ) { return a.d < b.d ; } void Clear () { top = 1 ;//下標從1開始 } int Empty () { return 1 == top ; } void Push ( type_d d , int num , int kind ) { heap[top] = Heap ( d , num , kind ) ; int o = top ++ ; while ( o > 1 && cmp ( heap[o] , heap[o >> 1] ) ) { swap ( heap[o] , heap[o >> 1] ) ; o >>= 1 ; } } int Front () { return heap[1].num ; } int Kind () { return heap[1].kind ; } void Pop () { heap[1] = heap[-- top] ; int o = 1 , p = o , l = o << 1 , r = o << 1 | 1 ; while ( 1 ) { if ( l < top && cmp ( heap[l] , heap[p] ) ) p = l ; if ( r < top && cmp ( heap[r] , heap[p] ) ) p = r ; if ( p == o ) break ; swap ( heap[o] , heap[p] ) ; o = p , l = o << 1 , r = o << 1 | 1 ; } } } ; struct Dij { Priority_Queue Q ; Edge edge[maxE] ; int adj[maxN] , cntE ; int done[2][maxN] ; type_d d[2][maxN] ; //int p[maxN] ; int cnt[2][maxN] ; void addedge ( int u , int v , int w = 0 ) { edge[cntE] = Edge ( v , w , adj[u] ) ; adj[u] = cntE ++ ; //edge[cntE] = Edge ( u , w , adj[v] ) ; //adj[v] = cntE ++ ; } void Init () { cntE = 0 ; Q.Clear () ; clear ( d , oo ) ; clear ( cnt , 0 ) ; clear ( adj , -1 ) ; //clear ( p , -1 ) ; } void Dijkstra ( int s ) { clear ( done , 0 ) ; d[0][s] = 0 ; cnt[0][s] = 1 ; Q.Push ( d[0][s] , s , 0 ) ; while ( !Q.Empty () ) { int u = Q.Front () ; int kind = Q.Kind () ; Q.Pop () ; if ( done[kind][u] ) continue ; done[kind][u] = 1 ; for ( int i = adj[u] ; ~i ; i = edge[i].n ) { int v = edge[i].v ; int val = d[kind][u] + edge[i].w ; if ( d[0][v] > val ) { d[1][v] = d[0][v] ; cnt[1][v] = cnt[0][v] ; Q.Push ( d[1][v] , v , 1 ) ; d[0][v] = val ; cnt[0][v] = cnt[kind][u] ; Q.Push ( d[0][v] , v , 0 ) ; } else if ( d[0][v] == val ) cnt[0][v] += cnt[kind][u] ; else if ( d[1][v] > val ) { d[1][v] = val ; cnt[1][v] = cnt[kind][u] ; Q.Push ( d[1][v] , v , 1 ) ; } else if ( d[1][v] == val ) cnt[1][v] += cnt[kind][u] ; } } } } ; Dij D ; void work () { int n , m ; int s , t ; int u , v , w ; D.Init () ; scanf ( "%d%d" , &n , &m ) ; REP ( i , m ) { scanf ( "%d%d%d" , &u , &v , &w ) ; D.addedge ( u , v , w ) ; } scanf ( "%d%d" , &s , &t ) ; D.Dijkstra ( s ) ; int ans = D.cnt[0][t] ; //printf ( "%d %d\n" , D.d[0][t] , D.d[1][t] ) ; if ( D.d[1][t] == D.d[0][t] + 1 ) ans += D.cnt[1][t] ; printf ( "%d\n" , ans ) ; } int main () { int T ; scanf ( "%d" , &T ) ; while ( T -- ) work () ; return 0 ; }
然後是另一種方法:
Dijkstra依舊求出最短路的數量,然後用DP在反圖上倒著記憶化搜尋,如果d[u] == d[v] + w[v,u],說明到目前為止還在最短路上,那麼可以在繼續遞迴中得到次短路的數量cnt1,則屬於該結點的次短路數量cnt += cnt1。如果d[u] == d[v] + w[v,u] - 1,說明正在次短路的一條邊上,後面一定都是最短路的邊了,則下面直接得到最短路數量cnt2,則次短路數量cnt += cnt2。感覺比上面的方法還要簡單~
程式碼如下:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std ; #define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i ) #define REP( i , n ) for ( int i = 0 ; i < n ; ++ i ) #define clear( A , X ) memset ( A , X , sizeof A ) typedef int type_d ; const int maxN = 1005 ; const int maxQ = 100005 ; const int maxH = 100005 ; const int maxE = 100005 ; const int oo = 0x3f3f3f3f ; struct Heap { int num ; type_d d ; Heap () {} Heap ( type_d D , int Num ) : d(D) , num(Num) {} } ; struct Edge { int v , n , w ; Edge () {} Edge ( int V , int W , int N ) : v(V) , w(W) , n(N) {} } ; struct Priority_Queue { Heap heap[maxH] ; int top ; void swap ( Heap &a , Heap &b ) { Heap tmp ; tmp = a ; a = b ; b = tmp ; } int cmp ( const Heap a , const Heap b ) { return a.d < b.d ; } void Clear () { top = 1 ;//下標從1開始 } int Empty () { return 1 == top ; } void Push ( type_d d , int num ) { heap[top] = Heap ( d , num ) ; int o = top ++ ; while ( o > 1 && cmp ( heap[o] , heap[o >> 1] ) ) { swap ( heap[o] , heap[o >> 1] ) ; o >>= 1 ; } } int Front () { return heap[1].num ; } void Pop () { heap[1] = heap[-- top] ; int o = 1 , p = o , l = o << 1 , r = o << 1 | 1 ; while ( 1 ) { if ( l < top && cmp ( heap[l] , heap[p] ) ) p = l ; if ( r < top && cmp ( heap[r] , heap[p] ) ) p = r ; if ( p == o ) break ; swap ( heap[o] , heap[p] ) ; o = p , l = o << 1 , r = o << 1 | 1 ; } } } ; struct Dij { Priority_Queue Q ; Edge edge[maxE] , E[maxE] ; int adj[maxN] , Adj[maxN] , cntE , cntEE ; int done[maxN] ; type_d d[maxN] ; //int p[maxN] ; int cnt[maxN] ;//最短路數量 int cnt2[maxN] ;//次短路數量 void addedge ( int u , int v , int w = 0 ) { edge[cntE] = Edge ( v , w , adj[u] ) ; adj[u] = cntE ++ ; E[cntEE] = Edge ( u , w , Adj[v] ) ; Adj[v] = cntEE ++ ; } void Init () { cntE = 0 ; cntEE = 0 ; Q.Clear () ; clear ( d , oo ) ; clear ( cnt , 0 ) ; clear ( cnt2 , -1 ) ; clear ( adj , -1 ) ; clear ( Adj , -1 ) ; //clear ( p , -1 ) ; } void Dijkstra ( int s ) { clear ( done , 0 ) ; d[s] = 0 ; cnt[s] = 1 ; Q.Push ( d[s] , s ) ; while ( !Q.Empty () ) { int u = Q.Front () ; Q.Pop () ; if ( done[u] ) continue ; done[u] = 1 ; for ( int i = adj[u] ; ~i ; i = edge[i].n ) { int v = edge[i].v ; int val = d[u] + edge[i].w ; if ( d[v] > val ) { d[v] = val ; cnt[v] = cnt[u] ; Q.Push ( d[v] , v ) ; } else if ( d[v] == val ) cnt[v] += cnt[u] ; } } } int dp ( int u ) { if ( cnt2[u] != -1 ) return cnt2[u] ; cnt2[u] = 0 ; for ( int i = Adj[u] ; ~i ; i = E[i].n ) { int v = E[i].v ; if ( d[u] == d[v] + E[i].w ) cnt2[u] += dp ( v ) ; else if ( d[u] == d[v] + E[i].w - 1 ) cnt2[u] += cnt[v] ; } return cnt2[u] ; } } ; Dij D ; void work () { int n , m ; int s , t ; int u , v , w ; D.Init () ; scanf ( "%d%d" , &n , &m ) ; REP ( i , m ) { scanf ( "%d%d%d" , &u , &v , &w ) ; D.addedge ( u , v , w ) ; } scanf ( "%d%d" , &s , &t ) ; D.Dijkstra ( s ) ; //printf ( "%d\n" , D.cnt[t] ) ; printf ( "%d\n" , D.cnt[t] + D.dp ( t ) ) ; } int main () { int T ; scanf ( "%d" , &T ) ; while ( T -- ) work () ; return 0 ; }