#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的代码,有所修改(为了让自己明白,嘿嘿)