最短路/sgu 103 Traffic Lights
题意
给出一个N个结点的无向图,每条边都有一个长度,但是每条边的两端有信号灯。信号灯有两种颜色:蓝色和紫色,并且信号灯有颜色变化的周期。当要走这条路,这条路的两端颜色一样时,才可以走,否则需要等到其中一个等变色后再走(走这条路的中途不用管灯的颜色)。给定两个点st,ed,求从st到ed最少的时间是多少。
输入:给出的灯的信息:C R Tb Tp。C表示开始时这个路口的灯的颜色,R表示当前这个颜色还要持续多久才会变成另一个颜色(为了方便描述,在后文记作“预热期”)。Tb表示这个路口的灯持续蓝色持续Tb,Tp同理。其实就是刚开始这个路口的颜色C会持续R秒,之后就开始有周期性的颜色交替了。
输出:最短时间 以及所经过的路口
分析
就是求一个有条件限制的最短路。
当对这条边进行松弛操作时,要算上等待到两端路口信号灯一致的时间所在的时刻,再加上这条路的长度。
这样,问题的主体就分析清楚了。但是需要处理的是到某个路口需要等多久才可以通过这条边。
我们记蓝色B为0,紫色P为1,t[i,0]存储的是B的周期,t[i,1]存储的是P的周期。
构造一个函数calctime,表示想要通过这条边,在这个路口需要等待的时间,先来看一下这个函数:
1 int calctime(int a,int b,int time,int f) 2 { 3 int ca,cb,ta,tb; 4 color(a,time,ca,ta);color(b,time,cb,tb); 5 if (ca==cb) return time; 6 if (ta==tb) 7 { 8 if (f==0) {return calctime(a,b,ta,1);} 9 else if (time<=r[a]||time<=r[b]) return calctime(a,b,ta,1); 10 else return INF; 11 } 12 return cmin(ta,tb); 13 }
其中,color(a,time,var ca,ta)表示在time时刻,a这个灯的颜色为ca,变成下一个颜色的时间点为ta。
显然,当a的颜色和b的颜色在time时刻相同时,即ca=cb时,不需要等待,直接返回当前时间点。
否则,当ca不等于cb时,我们需要等待其中一个灯变色,当ta不等于tb时,只需要返回其中较小的一个即可。
但是这题麻烦在,如果ca不等于cb,而ta=tb,即这两个灯当前颜色不一样,可是变成下一个颜色又在相同的时间时,该如何处理呢?
很显然,当这两个灯来来回回反复都是这样的话,说明这两个灯变色周期相同,则这两个灯永远达不到同时相等的时候,就是所谓的交替闪烁,成对称了。这条路不能通过。
但是,在ca不等于cb,ta=tb时,还有一种情况就是,其中一个或两个灯还在前面的“预热”状态,没有进入完整的循环周期,那么很有可能出现这种情况,但是这条路再等等还是可以通过的。
那么怎么判断呢?
很简单,当遇到需要等待的时间一样时,只需要再向下算一层。如果仍需要同样的时间,那么就成了“交替闪烁”了。否则,则是可以通过的。
至于实现,可以在calctime中再传递一个参数f,从主程序中传过去的f为0,表示第一次算,如果需要继续再算一层,则再函数中传递过去1,表示要计算下一层了。如果在计算第二层时,当前时间time在其中一个“预热”时间中,则说明这条路还有可能通过,否则这条路为对称的“交替闪烁”,永远过不去,返回一个最大值即可。
说完了calctime的实现,要具体说说color这个函数,即如何求解当前颜色以及变成下一个颜色的时间,先看看这个函数:
1 void color(int a,int time,int& ca,int& ta) 2 { 3 if (time<r[a]) {ta=r[a];ca=c[a];return;} 4 else 5 { 6 int temp=(time-r[a])%(t[a][0]+t[a][1]); 7 int now=time-temp; 8 if (c[a]==1) 9 { 10 if (temp<t[a][0]) {ca=0;ta=now+t[a][0];return;} 11 else {ca=1;ta=now+t[a][0]+t[a][1];return;} 12 } 13 else 14 { 15 if (temp<t[a][1]) {ca=1;ta=now+t[a][1];return;} 16 else {ca=0;ta=now+t[a][0]+t[a][1];return;} 17 } 18 } 19 }
很显然,当前时间time在“预热”时间内时,颜色未改变,为初始颜色,则这个灯变色的时刻即为r时刻;
否则,记录一个temp,表示当前在整个循环周期的哪一位置
如果初始颜色为P,则:
1、如果temp<Tb(B的循环周期),则当前颜色为B,且下一个变色时刻为time-temp+Tb的循环周期.如图:
2、如果temp超过了tb,则说明在紫色范围内,下一个变色点即为time+tb+tp-temp;
如果初始颜色为B,则同理。
主要的函数构造完毕,只剩下spfa来打个酱油,很好写,只是要注意再开个数组记录一下路径
注意事项 无解时输出0
至此,整道题可以解决了。
Accepted Code
1 /* 2 PROBLEM:sgu103 3 AUTHER:Rinyo 4 MEMO:最短路 5 */ 6 7 #include<cstdio> 8 #include<cstring> 9 10 int n,m,st,ed,INF; 11 int r[310],c[310],ans[310],dist[310],last[310]; 12 int t[310][2]; 13 int map[310][310]; 14 bool v[310]; 15 int q[30010]; 16 17 inline int cmin(const int &x,const int &y) {return x<y?x:y;} 18 19 void init() 20 { 21 scanf("%d%d%d%d",&st,&ed,&n,&m); 22 for (int i=1;i<=n;i++) 23 { 24 char ch[5]; 25 scanf("%s%d%d%d",ch,&r[i],&t[i][0],&t[i][1]); 26 if (ch[0]=='B') c[i]=0;else c[i]=1; 27 } 28 for (int i=1;i<=m;i++) 29 { 30 int x,y,z; 31 scanf("%d%d%d",&x,&y,&z); 32 map[x][y]=z;map[y][x]=z; 33 } 34 } 35 36 void color(int a,int time,int& ca,int& ta) 37 { 38 if (time<r[a]) {ta=r[a];ca=c[a];return;} 39 else 40 { 41 int temp=(time-r[a])%(t[a][0]+t[a][1]); 42 int now=time-temp; 43 if (c[a]==1) 44 { 45 if (temp<t[a][0]) {ca=0;ta=now+t[a][0];return;} 46 else {ca=1;ta=now+t[a][0]+t[a][1];return;} 47 } 48 else 49 { 50 if (temp<t[a][1]) {ca=1;ta=now+t[a][1];return;} 51 else {ca=0;ta=now+t[a][0]+t[a][1];return;} 52 } 53 } 54 } 55 56 int calctime(int a,int b,int time,int f) 57 { 58 int ca,cb,ta,tb; 59 color(a,time,ca,ta);color(b,time,cb,tb); 60 if (ca==cb) return time; 61 if (ta==tb) 62 { 63 if (f==0) {return calctime(a,b,ta,1);} 64 else if (time<=r[a]||time<=r[b]) return calctime(a,b,ta,1); 65 else return INF; 66 } 67 return cmin(ta,tb); 68 } 69 70 void spfa() 71 { 72 memset(v,false,sizeof(v)); 73 for (int i=1;i<=n;i++) dist[i]=INF; 74 int head=0,tail=1; 75 v[st]=true;q[1]=st;dist[st]=0;last[st]=0; 76 while (head<tail) 77 { 78 head++; 79 int now=q[head]; 80 for (int i=1;i<=n;i++) 81 { 82 if (map[now][i]>0) 83 { 84 int temp=calctime(now,i,dist[now],0); 85 if (temp>=INF) continue; 86 if (temp+map[now][i]<dist[i]) 87 { 88 dist[i]=temp+map[now][i]; 89 last[i]=now; 90 if (!v[i]) {tail++;q[tail]=i;v[i]=true;} 91 } 92 } 93 } 94 v[q[head]]=false; 95 } 96 if (dist[ed]>=INF) {printf("0");return;} 97 printf("%d\n",dist[ed]); 98 int now=ed,len=0; 99 while (now!=0) {len++;ans[len]=now;now=last[now];} 100 for (int i=len;i>=2;i--) printf("%d ",ans[i]); 101 printf("%d\n",ans[1]); 102 return; 103 } 104 int main() 105 { 106 freopen("sgu103.in","r",stdin); 107 freopen("sgu103.out","w",stdout); 108 INF=99999999; 109 init(); 110 spfa(); 111 return 0; 112 }