题型:枚举+最短路
题目大意:
给你一个无向图,n个点,m条普通边,k条特殊边,s为起点,t为终点。
定义(u,v,w)为一条连接u,v,花费为w的边。
现在你需要从u走到v,途中最多只能经过一次特殊边的路径,转特殊边的起点(没有输出Ticket Not Used),以及最小花费。
题目分析:
设d[0][i]为以s为起点到i的最小花费,d[1][i]为以t为起点到i的最小花费。
首先用普通边构造出图G,最短路求出G中以s到所有点的最短路以及t到所有点的最短路,然后枚举每一条特殊边(u,v,w),则ans = min{d[0][u] + d[1][v] + w}。
然后路径就在求最短路的时候顺便求一下就好了。
PS:本题出于作者习惯,Dij一律为附带手敲优先队列版本
代码如下:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std ; #define clear( A , X ) memset ( A , X , sizeof A ) #define REP( i , n ) for ( int i = 0 ; i < n ; ++ i ) #define FF( i , a , b ) for ( int i = a ; i <= b ; ++ i ) const int maxN = 505 ; const int maxQ = 20005 ; const int maxH = 1000005 ; const int maxE = 1000005 ; const int oo = 0x3f3f3f3f ; struct Heap { int num , d ; Heap () {} Heap ( int 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 = a ; a = b ; b = tmp ; } int cmp ( const Heap a , const Heap b ) { return a.d < b.d ; } void Clear () { top = 0 ; } int Empty () { return 0 == top ; } void Push ( int d , int num ) { heap[top] = Heap ( d , num ) ; int o = top ++ , p = ( o - 1 ) >> 1 ; while ( o && cmp ( heap[o] , heap[p] ) ) { swap ( heap[o] , heap[p] ) ; o = p , p = ( o - 1 ) >> 1 ; } } int Front () { return heap[0].num ; } void Pop () { heap[0] = heap[-- top] ; int o = 0 , p = o , l = o * 2 + 1 , r = o * 2 + 2 ; 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 * 2 + 1 , r = o * 2 + 2 ; } } } ; struct Dij { Priority_Queue Q ; Edge edge[maxE] ; int adj[maxN] , cntE ; int done[maxN] ; int d[2][maxN] ; int p[2][maxN] ; void Addedge ( int u , int v , int w ) { 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 ( p , -1 ) ; clear ( adj , -1 ) ; } void Dijkstra ( int s , int t , int dis[] , int pre[] ) { clear ( done , 0 ) ; dis[s] = 0 ; Q.Push ( dis[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 ; if ( dis[v] > dis[u] + edge[i].w ) { pre[v] = u ; dis[v] = dis[u] + edge[i].w ; Q.Push ( dis[v] , v ) ; } } } } void Print1 ( int t , int i , int pre[] ) { if ( ~pre[i] ) Print1 ( t , pre[i] , pre ) ; printf ( "%d%c" , i , ( i != t ) ? ' ' : '\n' ) ; } void Print2 ( int i , int pre[] ) { if ( ~pre[i] ) Print2 ( pre[i] , pre ) ; printf ( "%d " , i ) ; } void Print3 ( int i , int pre[] ) { printf ( "%d%c" , i , ( ~pre[i] ) ? ' ' : '\n' ) ; if ( ~pre[i] ) Print3 ( pre[i] , pre ) ; } } ; Dij D ; void work () { int n , m , k ; int u , v , w ; int s , t ; int idu , idv ; int cas = 0 ; while ( ~scanf ( "%d%d%d" , &n , &s , &t ) ) { if ( cas ++ ) printf ( "\n" ) ; D.Init () ; scanf ( "%d" , &m ) ; REP ( i , m ) { scanf ( "%d%d%d" , &u , &v , &w ) ; D.Addedge ( u , v , w ) ; } D.Dijkstra ( s , t , D.d[0] , D.p[0] ) ; D.Dijkstra ( t , s , D.d[1] , D.p[1] ) ; int ans = D.d[0][t] ; int flag = 0 ; scanf ( "%d" , &k ) ; REP ( i , k ) { scanf ( "%d%d%d" , &u , &v , &w ) ; if ( ans > D.d[0][u] + D.d[1][v] + w ) { ans = D.d[0][u] + D.d[1][v] + w ; idu = u , idv = v ; flag = 1 ; } if ( ans > D.d[1][u] + D.d[0][v] + w ) { ans = D.d[1][u] + D.d[0][v] + w ; idu = v , idv = u ; flag = 1 ; } } //printf ( "%d %d\n" , D.d[0][5] , D.d[1][6] ) ; if ( !flag ) { D.Print1 ( t , t , D.p[0] ) ;//s->t printf ( "Ticket Not Used\n" ) ; printf ( "%d\n" , ans ) ; } else { D.Print2 ( idu , D.p[0] ) ;//s->idu D.Print3 ( idv , D.p[1] ) ;//idv->t printf ( "%d\n" , idu ) ; printf ( "%d\n" , ans ) ; } } } int main () { work () ; return 0 ; }