传送门:【SGU】103. Traffic Lights
题目大意:
在一个N个点M条边的城市里,每个点上有一个交通灯,交通灯亮B、P颜色,在离开始r的时间内交通灯会亮c颜色(c为B、P中的一种),然后开始交替亮B、P颜色,B颜色亮b时间,P颜色亮p时间。M条边经过每条边都有个时间,要走一条边必须要满足这个时刻这条边的两个端点颜色相同,当你正在边上走时两端为什么颜色对你无影响。现在你要从给定的起点s走到终点t,问最小的花费以及路径。
题目分析:
首先我们要明白到达一个点的时间一定是越早越好,所以我们就可以跑最短路了。然后,我们重点要求的就是从一个点到另一个点的花费。
如果我们知道时间,那么就可以得到一个点此时的颜色以及转化过去的最小时间花费。
假设我们现在要经过边(u,v)。
设此时的时间为time_now,u现在颜色为cu_now,v现在颜色为cv_now,u变为下一颜色时的时间为cu_next_time,v变为下一颜色时的时间为cv_next_time。
显然:
1.当cu_now = cv_now时此时u和v的颜色相同,这个时刻我们就可以走这条边。
2.当cu_now != cv_now时,如果cu_next_time != cv_next_time那么我们取其中较小的一个就好。
3.当cu_now != cv_now且cu_next_time == cv_next_time时,如果u的B颜色周期等于v的P颜色周期且u的P颜色周期等于v的B颜色周期,那么返回INF表示这条边的两个端点的颜色永远不会重叠,因此也无法经过这条边。
4.如果3不满足,说明:
(1)要么u的B颜色周期不等于v的P颜色周期。
(2)要么u的P颜色周期不等于v的B颜色周期。
如果(1)满足那么我们用cu_next_time代入一开始给的时间然后重新计算一遍就好(因为下一次的cu_next_time != cv_next_time,直接眺转至2)。
如果(2)满足那么我们将(1)重复进行两遍就好(因为第一次代入cu_next_time = cv_next_time而第二次才不同,眺转至2)。
我们用一个递归程序实现上面的操作,递归层数不超过2所以可以视为常数。
接下来是另一关键性问题:怎么求一个节点u的下一个颜色以及变为下一颜色时的最短时间?
设time_now为当前时间,color_now为此时颜色,color_next_time为变为下一颜色的最短时间。
令color_pre为节点u还未进入循环前的颜色,r为未进入循环前color_pre的持续时间,b为B颜色的持续时间,p为P颜色的持续时间。
那么当time_now小于进入循环前的持续时间时,color_now就等于初始颜色color_pre,而color_next_time就为r。
如果time_now>=r,那么令tmp = ( time_now - r ) % ( b + p )。
下面分类讨论:
1.如果color_pre为B颜色,且此时tmp<p,则当前颜色为B,时间为time_now + p - tmp。
2.如果color_pre为B颜色,且此时tmp<b,则当前颜色为P,时间为time_now + b + p - tmp。
3.如果color_pre为P颜色,且此时tmp<b,则当前颜色为P,时间为time_now + b - tmp。
4.如果color_pre为P颜色,且此时tmp<p,则当前颜色为B,时间为time_now + b + p - tmp。
既然上面的问题都解决了,那么我们就知道经过一条边的花费了,于是接下来便用最短路算法完成。记得记录路径。
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std ; #pragma comment(linker, "/STACK:16777216") #define rep( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i ) #define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i ) #define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i ) #define clr( a , x ) memset ( a , x , sizeof a ) #define cpy( a , x ) memcpy ( a , x , sizeof a ) const int MAXN = 305 ; const int MAXE = 100005 ; const int INF = 0x3f3f3f3f ; struct Traffic { int color_pre , r , b , p ; void input () { char color[2] ; scanf ( "%s%d%d%d" , color , &r , &b , &p ) ; color_pre = color[0] == 'B' ? 0 : 1 ; } } ; struct Edge { int v , c , n ; Edge () {} Edge ( int v , int c , int n ) : v ( v ) , c ( c ) , n ( n ) {} } ; Traffic node[MAXN] ; Edge E[MAXE] ; int H[MAXN] , cntE ; int d[MAXN] ; int vis[MAXN] ; int pre[MAXN] ; int Q[MAXN] , head , tail ; int S[MAXN] , top ; int s , t ; int n , m ; void clear () { cntE = 0 ; clr ( H , -1 ) ; } void addedge ( int u , int v , int c ) { E[cntE] = Edge ( v , c , H[u] ) ; H[u] = cntE ++ ; } void get_color ( int u , int time_now , int& color_now , int& color_next_time ) { if ( time_now < node[u].r ) {//in pre color_now = node[u].color_pre ; color_next_time = node[u].r ; } else {//in loop int tmp = ( time_now - node[u].r ) % ( node[u].b + node[u].p ) ; if ( node[u].color_pre == 0 ) { if ( tmp < node[u].p ) { color_now = 1 ; color_next_time = time_now + node[u].p - tmp ; } else { color_now = 0 ; color_next_time = time_now + node[u].b + node[u].p - tmp ; } } else { if ( tmp < node[u].b ) { color_now = 0 ; color_next_time = time_now + node[u].b - tmp ; } else { color_now = 1 ; color_next_time = time_now + node[u].b + node[u].p - tmp ; } } } } int get_cost ( int u , int v , int time_now ) { int cu_now , cv_now , cu_next_time , cv_next_time ; get_color ( u , time_now , cu_now , cu_next_time ) ; get_color ( v , time_now , cv_now , cv_next_time ) ; if ( cu_now == cv_now ) return time_now ;//1 if ( cu_next_time == cv_next_time ) { if ( node[u].b == node[v].p && node[u].p == node[v].b ) return INF ;//3 return get_cost ( u , v , cu_next_time ) ;//4 } return min ( cu_next_time , cv_next_time ) ;//2 } void spfa () { clr ( d , INF ) ; clr ( vis , 0 ) ; head = tail = 0 ; Q[tail ++] = s ; pre[s] = 0 ; d[s] = 0 ; while ( head != tail ) { int u = Q[head ++] ; if ( head == MAXN ) head = 0 ; vis[u] = 0 ; for ( int i = H[u] ; ~i ; i = E[i].n ) { int v = E[i].v ; int cost = E[i].c + get_cost ( u , v , d[u] ) ; if ( d[v] > cost ) { d[v] = cost ; pre[v] = u ; if ( !vis[v] ) { vis[v] = 1 ; Q[tail ++] = v ; if ( tail == MAXN ) tail = 0 ; } } } } if ( d[t] == INF ) printf ( "0\n" ) ;//not exist a path else { printf ( "%d\n" , d[t] ) ; top = 0 ; int x = t ; while ( x ) { S[top ++] = x ; x = pre[x] ; } rev ( i , top - 1 , 0 ) printf ( "%d%c" , S[i] , i ? ' ' : '\n' ) ; } } void solve () { clear () ; int u , v , c ; scanf ( "%d%d" , &n , &m ) ; For ( i , 1 , n ) node[i].input () ; rep ( i , 0 , m ) { scanf ( "%d%d%d" , &u , &v , &c ) ; addedge ( u , v , c ) ; addedge ( v , u , c ) ; } spfa () ; } int main () { while ( ~scanf ( "%d%d" , &s , &t ) ) solve () ; return 0 ; }