#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define maxn 333 #define INF 0x7fffffff #define min(a,b) ((a<b)?a:b) struct node{ int r,tb,tp;//r表示刚开始r时间后变换灯,tb表示蓝灯持续时间,tp是紫灯 char color[2];//刚开始灯的颜色 }e[maxn]; int map[maxn][maxn],start,en,m,n,d[maxn],s[maxn],pre[maxn]; void find(int time,int x,int &px,char sx[])//px表示还要多久要变换,sx始现在的颜色 { if(time<e[x].r) { px=e[x].r-time; sx[0]=e[x].color[0]; } else { int t; t=(time-e[x].r)%(e[x].tb+e[x].tp); if(e[x].color[0]=='B') { if(t<e[x].tp) px=e[x].tp-t,sx[0]='P'; else px=e[x].tb+e[x].tp-t,sx[0]='B'; } else { if(t<e[x].tb) px=e[x].tb-t,sx[0]='B'; else px=e[x].tb+e[x].tp-t,sx[0]='P'; } } } int findtime(int time,int x,int y,int num) { if(num>3)//同时变换后还要再找时间,3次同时变换说明死循环,这条路不能通过 /* 循环两次的例子,现在时间是15,一点P 1 16 25,第一次变换(P,17),第二次变换(B,45),第三次变换(P,58) 二点P 17 25 15,第一次变换(P,17),第二次变换(B,45),第三次变换(P,57) */ return -1; int px,py;//x,y点,还要灯变化还需要的时间 char sx[2],sy[2];//x,y点灯的颜色 find(time,x,px,sx); find(time,y,py,sy); if(sx[0]==sy[0]) return time; if(px==py) return findtime(time+px,x,y,num+1);//同时变换后再找变换点 time+=min(px,py);//较小的变换时间,就是到那个店的最小时间 return time; } void dij()//用最段路径求解 { int i,j,k,time; for(i=0;i<n;i++) { int min=INF,u; for(j=1;j<=n;j++) if(s[j]==0&&d[j]<min) { min=d[j]; u=j; } s[u]=1; for(j=1;j<=n;j++) { if(map[u][j]!=-1&&s[j]==0) { time=findtime(d[u],u,j,1); if(time==-1) continue; time+=map[u][j]; if(time<d[j]) { d[j]=time; pre[j]=u; } } } /* for(j=1;j<=n;j++) printf("%11d",d[j]); printf("\n");*/ } } int main() { int i,j,k; scanf("%d%d",&start,&en); scanf("%d%d",&n,&m); for(i=1;i<=n;i++) d[i]=INF; memset(map,-1,sizeof(map)); memset(s,0,sizeof(s));//标记走过的节点 memset(pre,-1,sizeof(pre));//记录路径 for(i=1;i<=n;i++) scanf("%s%d%d%d",e[i].color,&e[i].r,&e[i].tb,&e[i].tp); for(i=0;i<m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); map[a][b]=map[b][a]=c; } d[start]=0; pre[start]=start; dij(); if(pre[en]==-1) printf("0\n"); else{ printf("%d\n",d[en]); int p,q,t=0,load[maxn]; p=en; while(p!=pre[p])//路径输出 { load[t++]=p; p=pre[p]; } printf("%d",start); for(i=t-1;i>=0;i--) printf(" %d",load[i]); printf("\n"); } return 0; } /* 题意: 知道始末两点,各点间所需时间,求两点间的最短路径。 各点蓝紫两色灯,循环变换 其中两点间可通过的条件是:两点的灯色相同 方法就是在最短路径中换算下一节点是加上两点灯色变换的时间。 原理: 给的图是一个动态的图,边权非负且在发生周期性的变化。要求两点之间的最短路。 考虑Dijstra还能不能用。Dijstra的过程是不断将距离源点最近的点加入集合S, 直到目标点也被加入S。Dijstra要求随着路径上节点数目的增加,路径长度也相应增加,即不能出现负权边。 他的贪心基于这样的考虑:若能尽早到达某个点,就尽早到达,尽早到达这个点时扩展出的经过这个点的路径, 比较晚达到这个点时扩展出的经过这个点的路径短。这个题中,边权的变化是有规律的,我发现尽早到达的想法仍然正确。 所以Dijstra可以用。 下面想想怎么求某一时刻的边权。 边权有两个组成部分:一部分是通过这条道路需要的时间,另一部分是在路口等待的时间。 给出时刻t,可以求得道路两端的灯的颜色。 若颜色相同,则等待时间即为0;若颜色不同,则需等到某一个灯换颜色为止,特殊的,两个灯同时换了颜色, 则需要再等某个灯换颜色,如果出现两个灯连续三次同时变换颜色,则这条道路永远无法通行: 设某时刻灯A为紫色,灯B为蓝色。第一次同时变色后,灯A为蓝色,灯B为紫色。而后两灯第二次同时变色, 说明灯A的蓝色周期与灯B的紫色周期相等。第三次同时变色,说明灯A的紫色周期与灯B的蓝色周期相等。 即,两灯颜色变换的总周期相同而相位相反,说明这条道路无法通行了。 */
转自:http://d.ream.at/sgu-103/的说明和http://hi.baidu.com/wqqr_code/item/c142501a4583c4071894ecf3的代码,有所修改(为了让自己明白,嘿嘿)