题目概述
城市中有N个路口,路口之间有M条双向路,每个路口都有一个信号灯,颜色分蓝(B),紫(P)两种,分别有各自的持续时间,只有当两个路口灯同色时这段路才能通行,一辆车从路口source出发,要到路口target,给定出发时路口灯色,剩余时间,路口蓝灯和紫灯持续时间,问最快多久能到
如果车到路口时刚好变灯,那就得等下一次了
时限
1000ms/3000ms
输入
第一行两个整数source,target,第二行两个整数N,M,其后N行,每行一个字符,代表出发时灯色,三个整数,当前灯剩余时间r,蓝灯持续时间B,紫灯持续时间P,其后M行,每行三个数a,b,c,为路连接的两个路口以及途经用时,输入只有一组
限制
2<=N<=300;1<=M<=14000;1<=r<=B,P<=100;1<=c<=100
输出
每行一个数,为所求最少用时,若无法到达则为0
样例输入
1 4
4 5
B 2 16 99
P 6 32 13
P 2 87 4
P 38 96 49
1 2 4
1 3 40
2 3 75
2 4 76
3 4 77
样例输出
127
讨论
图论,单源最短路,bellman_ford队列优化算法,需要稍微处理一点,在松弛之前算出需要等多长时间,之后松弛,另外需要注意,可能会出现两个路口灯的持续时间都相同,但颜色相反,这种情况下这条路永远没法走,因而当算得等待时间加上途经用时已经无法松弛时,就不用再算了,剩下的部分,就是最短路了
从实现层面上,主要还是处理等待时间,将到达路口的时间加上最初的剩余时间,对灯循环一次的周期求模,由此判断当前灯的状态,若要等待,则等待两个路口最先变灯的时间,然后再计算判断
这个题用的静态邻接表写的,也算比较好的
题解状态
484K,47MS,C++,1699B
题解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x3f3
#define MAXN 302
#define memset0(a) memset(a,0,sizeof(a))
int source, target, N, M;//起点 终点 路口数 路数
int r[MAXN], B[MAXN], P[MAXN];//剩余时间 蓝灯持续时间 紫灯持续时间
int to[28005], w[28005], pre[28005], index = 1, al[MAXN], dis[MAXN];//路的终点 途经用时 previous 上一个节点的下标 index 为邻接表提供下标 adjacency_list 邻接表最后一个节点的下标 distance 最短路用时
queue<int>q;//辅助队列
bool inq[MAXN];//辅助数组 在队列中
int fun()
{
for (int p = 1; p <= N; p++) {
char ch;
getchar();//吞掉上一行的换行
scanf("%c%d%d%d", &ch, &r[p], &B[p], &P[p]);//input
if (ch == 'B')
r[p] = B[p] - r[p];//求出当前的灯亮起的时间 这里默认每个周期是先蓝后紫
else
r[p] = B[p] + P[p] - r[p];//紫灯则以整个周期扣掉剩余时间
}
for (int p = 0; p < M; p++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);//input
to[index] = b, w[index] = c, pre[index] = al[a], al[a] = index++;
to[index] = a, w[index] = c, pre[index] = al[b], al[b] = index++;
}
for (int p = 1; p <= N; p++)//算法初始化
dis[p] = INF;
dis[source] = 0;
q.push(source);
while (!q.empty()) {
int a = q.front();
q.pop();
inq[a] = 0;
for (int p = al[a]; p; p = pre[p]) {
int wait = 0;//等待时间
while (dis[to[p]] > dis[a] + w[p] + wait) {//为了防止死循环 同样也是剪枝 如果加上等待时间后已经没法松弛就算了
int timea = (dis[a] + r[a] + wait) % (B[a] + P[a]);//当前路口的灯亮起时间
int timeb = (dis[a] + r[to[p]] + wait) % (B[to[p]] + P[to[p]]);//下一个路口灯亮起时间
if ((timea < B[a]) != (timeb < B[to[p]]))//如果灯色不同
wait += min(timea < B[a] ? B[a] - timea : B[a] + P[a] - timea, timeb < B[to[p]] ? B[to[p]] - timeb : B[to[p]] + P[to[p]] - timeb);//将最近变灯的时间加到等待时间
else//同色就不用等了
break;
}
if (dis[to[p]] > dis[a] + w[p] + wait) {
dis[to[p]] = dis[a] + w[p] + wait;
if (!inq[to[p]]) {
q.push(to[p]);
inq[to[p]] = 1;
}
}
}
}
return dis[target] == INF ? 0 : dis[target];//当无法到达的时候就只有INF了 输出0
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
scanf("%d%d%d%d", &source, &target, &N, &M);//input
printf("%d\n", fun());//output
}
EOF