问题:
Dijstra算法变形题,有向边分正行和逆行方向,注意逆行的绿灯时间是正行的红灯时间。
这题的关键是理清从当前节点出发,到下一个节点是哪一时刻,理清这一点后,再跑Dijstra算法求最短路。
假设curr_t时刻到达u节点,到达邻居v的时刻为nei_t。
无论是正行还是逆行,红绿灯的周期T = g + y + r + y,因此curr_t时刻红绿灯的状态等价于p=curr_t%T 时刻的状态
根据p(即红绿灯的状态)分类讨论:
1.走正行道,绿黄红黄顺序等红绿灯, g y r y :
①:p < g, 到达邻居的时间nei_t = curr_t + y,即当前时间加上到达邻居节点的时间y(也是黄灯时间)
②:p >= g, 到达邻居的时间nei_t = curr_t + (g + r + y + y - p) + y,(g + r + y + y - p)为等待绿灯的时间
2.走逆行道,红黄绿黄顺序等红绿灯, r y g y:
①:p < r + y,到达邻居的时间nei_t = curr_t + (r + y - p) + y,(r + y - p)是等绿灯的时间
②:p>=r+y && p < r + y + g, 到达邻居的时间nei_t = curr_t + y,无需等待绿灯
③:p>=r+y+g:到达邻居的时间nei_t = curr_t + (r + y + g + y - p + r + y) + y,
情况③比较特殊,需要等待当前周期结束(即r + y + g + y - p),再等下一个周期的红灯和黄灯(r + y)
#include <iostream>
#include <bits/stdc++.h>
#define ll long long int
using namespace std;
const int MAX = 100010;
struct Edge {
// dir为true表示正行
int to, ne, g, r, y;
bool dir;
Edge() {}
Edge(int to, int ne, int g, int r, int y, bool dir) : to(to), ne(ne), g(g), r(r), y(y), dir(dir) {}
} e[3000000];
struct Node {
int n;
ll t;
Node(int n, ll t) : n(n), t(t) {}
bool operator < (const Node &n1) const {
return t > n1.t;
}
};
int cnt = 1;
int h[MAX] = {0};
void add(int u, int v, int g, int r, int y, bool dir) {
e[cnt].ne = h[u];
e[cnt].to = v;
e[cnt].g = g;
e[cnt].r = r;
e[cnt].y = y;
e[cnt].dir = dir;
h[u] = cnt++;
}
int f[MAX];
int find(int x) {
return x == f[x] ? f[x] : (f[x] = find(f[x]));
}
int main()
{
// 请在此输入您的代码
int n, m, src, tar;
int u, v, g, r, d;
cin >> n >> m >> src >> tar;
ll t[MAX] = {0};
for(int i = 0; i < MAX; i++) {
f[i] = i;
t[i] = 0x1fffffffffffffff;
}
for(int i = 0; i < m; i++) {
scanf("%d %d %d %d %d", &u, &v, &g, &r, &d);
add(u, v, g, r, d, true);
// 逆行的绿灯时间是正行的红灯时间
add(v, u, r, g, d, false);
int fx = find(u);
int fy = find(v);
if(fx != fy) f[fx] = fy;
}
if(find(src) != find(tar)) {
cout << -1;
return 0;
}
priority_queue<Node, vector<Node>> pq;
t[src] = 0;
pq.push({src, 0});
while(!pq.empty()) {
int curr = pq.top().n;
ll curr_t = pq.top().t;
pq.pop();
if(curr == tar) {
cout << curr_t;
return 0;
}
for(int edge = h[curr]; edge; edge = e[edge].ne) {
bool dir = e[edge].dir;
int to = e[edge].to;
ll g = e[edge].g;
ll r = e[edge].r;
ll y = e[edge].y;
ll nei_t;
ll p = curr_t % (g + r + y + y);
if(dir) {
// 走正行道,绿黄红黄顺序等红绿灯, g y r y
nei_t = p < g ? (curr_t + y) : (curr_t + g + r + y + y - p + y);
} else {
// 走逆行道,红黄绿黄顺序等红绿灯 r y g y
if(p < r + y) nei_t = curr_t + r + y - p + y;
else if(p < r + y + g) nei_t = curr_t + y;
else nei_t = curr_t + r + y + g + y - p + r + y + y;
}
if(nei_t < t[to]) {
t[to] = nei_t;
pq.push({to, nei_t});
}
}
}
return 0;
}