SGU 103.Traffic Lights(最短路)
时间: 0.50 second(s)
空间: 4096 kilobytes
输入: 标准输入
输出: 标准输出
Dingiville 城市的交通规则非常奇怪,城市公路通过路口相连,两个不同路口之间最多只有一条直达公路。公路的起止点不会是同一路口。在任意一条公路上顺不同方向走所需 时间相同。每一个路口都有交通灯,这些交通灯在某一时刻要么是蓝色,要么是紫色。同一个灯上2个颜色的维持时间受到定期调控,总是蓝色持续一段时间,紫色 持续一段时间。交通规则规定两个路口可以通车仅当公路两边交通灯的颜色相同(也就是说只要你在A城市看见A与B的交通灯颜色相同,那么你就可以走上A-B 这条路并到达B点)。交通工具可以在路口等候。现在你有这个城市的地图,包含:
• 通过所有公路需要的时间(整数)
• 每个路口交通灯两种颜色的持续时间(整数)
• 每个路口交通灯的初始颜色以及初始颜色的持续时间(整数).
你的任务是找到一条从起点到终点的最快路径,当有多条这样的路径存在时,你只需输出任意一条即可。
输入
第一行包含两个整数: 起点和终点的编号. 第二行包含两个整数: N, M. 接下来 N 行描述 N 个路口的情况.
第 (i+2) 行 是 i 号路口的信息: Ci, riC, tiB, tiP 。其中字符 Ci 要么是“B”代表蓝色,要么是“P”代表紫色,表示 i 的 起始颜色. riC, tiB, tiP 分别表示初始颜色,蓝色,紫色的持续时间, 1 ≤ tiC ≤ 100 . 1 ≤ riC ≤ tiC 。最后的 M 行表示关于 M 条公路的信息。包含: i, j, lij 。i 和 j 号路口被这条路连接. 2 ≤ N ≤ 300 表示路口数. 路口从 1 到 N 编号. 1 ≤ M ≤ 14000 公路从 1 到 M 编号. 1 ≤ lij ≤ 100 表示 lij 是通过i到j这条公路需要的时间。
输出
如果存在最短路径:
• 第一行输出最短时间。
• 第二行是一个对应第一行答案的路口编号表,从起点到终点输出路口编号,数字之间用空格隔开。
如果不存在:
• 输出“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
1 2 4
{=======================}
分析:
很明显的一个最短路问题,但是比较麻烦的是距离的计算
如下图:
对于两个城市的信号灯的变化,可以在时间轴上画出,黑色部分是当前颜色剩余时间Ci,当Ci 等于0时,其实相当于进入了下一种颜色,并且Ci为下一种颜色的最大时间,即持续时间。
如果两个信号灯不是初始一样,就是在其中一个灯比另一个灯多变化1次后一样。如果两个信号灯总是同时变化,并且初始颜色不同的话,那么就是不能通过。
可以用两个函数,来实现计算,一个为afterT(u,T)得到在T时间后,u灯的颜色和当前颜色的剩余时间,
另一个为getT(u,v),得到为了从路口u到路口v最少需要的时间,这里在计算从u到v的时间时,要把u和v两个路口的时间轴的起点调到从起点到u的最短时间。
大概的框架就是SPFA加上 计算函数afterT()和getT();
下面要处理一些细节,让程序更加好写。
先来考虑,什么时候两个路口间的道路是不能通过的?
之前说过,如果两个信号灯总是同时变化,并且初始颜色不同的话,那么就是不能通过。那么就是说如果(u,v)间不能通过,一定满足Cu == Cv ,
并且满足BTu ==PTv 和 PTu ==BTv ,如果确定两个路口不能互相到达,我们直接删除两个路口间的边,方便计算getT()。
在处理了不能通过的情况下, getT(u,v)简单地枚举每次最近的变化就好了,最多2次,就可以得到等待时间waitT,而我们需要的返回值应该是
waitT+Edge[u][v];Edge[u][v]是通过连接(u,v)的路的时间。
伪代码在这:
int getT ( u, v) { u = afterT (u, 到达u的时间), v = afterT (v, 到达u的时间); int m1 = u当前颜色剩余时间, m2 = 当前颜色剩余时间, waitT = 0; while (u当前颜色 != v当前颜色) { if (m1 < m2) waitT += m1, u变颜色; else if (m1 > m2) waitT += m2, v变颜色; else { waitT += m1, u,v同时变颜色; m1 = 当前颜色持续时间, m2 = 当前颜色持续时间; } } return waitT + 通过(u,v)的时间; }
至于afterT()就非常简单了,从前往后一个一个推就行了
参考代码:
#include <cstdio> #include <queue> using namespace std; const int INF = 311; struct data { int sta, ti; } f[INF]; queue<int> ql; //dTi[i]记录从起点到i的最短时间,pr[v]指向上一个路口,tBP记录信号灯的持续时间 int Edge[INF][INF], tBP[INF][2], dTi[INF], pd[INF], pr[INF]; int n, m, S, A, x, y, z; //计算T时间后p的颜色,和当前颜色的剩余时间 data afterT (int p, int t) { data x = f[p]; while (t) { if (t <= x.ti) { x.ti -= t; t = 0; } else { t -= x.ti; x.sta ^= 1; x.ti = tBP[p][x.sta]; } } if (x.ti == 0) { x.sta ^= 1; x.ti = tBP[p][x.sta]; } return x; } //计算从经过u到v的最少需要的时间 int getT (int u, int v) { data uSt = afterT (u, dTi[u]), vSt = afterT (v, dTi[u]); int m1 = uSt.ti, m2 = vSt.ti, waitT = 0; while (uSt.sta != vSt.sta) { if (m1 < m2) waitT += m1, uSt.sta ^= 1; else if (m1 > m2) waitT += m2, vSt.sta ^= 1; else { waitT += m1, uSt.sta ^= 1, vSt.sta ^= 1; m1 = tBP[u][uSt.sta], m2 = tBP[v][vSt.sta]; } } return waitT + Edge[u][v]; } //最短路 void SPFA() { dTi[S] = 0, pd[S] = 1, ql.push (S); while (!ql.empty() ) { int x = ql.front(), k; ql.pop(); for (int i = 1; i <= n; i++) { if (i == x || Edge[x][i] == 0) continue; k = getT (x, i); if (dTi[i] == 0 || dTi[x] + k < dTi[i]) { dTi[i] = dTi[x] + k, pr[i] = x; if (pd[i] == 0 && i != A) pd[i] = 1, ql.push (i); } } pd[x] = 0; } } //递归输出路径 void write (int x) { if (x != S) write (pr[x]); printf ("%d ", x); } int main() { scanf ("%d%d", &S, &A); scanf ("%d%d", &n, &m); char ch = getchar(); for (int i = 1; i <= n; i++, getchar() ) { scanf ("%c %d %d %d", &ch, &f[i].ti, &tBP[i][0], &tBP[i][1]); if (ch == 'B') f[i].sta = 0; else f[i].sta = 1; if (f[i].ti == 0) { f[i].sta ^= 1; f[i].ti = tBP[i][f[i].sta];} } for (int i = 1; i <= m; i++) { scanf ("%d%d%d", &x, &y, &z); Edge[x][y] = Edge[y][x] = z; } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { if (i == j || Edge[x][y] == 0) continue; if (f[i].sta != f[j].sta && f[i].ti == f[j].ti && tBP[i][0] == tBP[j][1] && tBP[i][1] == tBP[j][0]) Edge[i][j] = Edge[j][i] = 0; } SPFA(); printf ("%d\n", dTi[A]); if (dTi[A]) write (A); return 0; }
http://www.cnblogs.com/keam37/ keam所有 转载请注明出处