main
黄海 2 years ago
parent 90742f907a
commit aa090091c7

@ -1,7 +1,7 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 10010, M = N << 1, K = 110; // 节点个数上限,边数上限,发车间隔时长上限
const int INF = 0x3f3f3f3f; // Dijsktra专用正无穷
const int INF = 0x3f3f3f3f;
struct Node {
int u, r, d; // u节点编号r状态d最短到达时间
@ -22,14 +22,14 @@ dis[u][i]表示到达第u处地点并且到达时间mod k = i的情况下的
*/
int dis[N][K], st[N][K];
int main() {
memset(h, -1, sizeof h);
memset(h, -1, sizeof h); // 初始化链式前向星
int n, m, k;
cin >> n >> m >> k;
for (int i = 0; i < m; i++) {
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c); // 从a到b建一条边c权值为开放时间
add(a, b, c); // 从a到b建一条边c此路径的开放时间
}
// 初始状态1号点在状态0时最短距离为0其它点的最短距离为无穷大
@ -49,14 +49,15 @@ int main() {
int r = q.top().r; // 节点状态r
q.pop();
if (st[u][r]) continue; // 该状态已经加入到集合中,也就是已经被搜索过
if (st[u][r]) continue;
// 该状态已经加入到集合中,也就是已经被搜索过
// 先被搜索过在队列宽搜中意味着已经取得了最短路径
st[u][r] = 1;
for (int i = h[u]; ~i; i = ne[i]) { // 枚举邻接点v和连接到v节点道路的开放时间
int v = e[i]; // v节点也就是下一个节点
int t = w[i]; // v节点的开放时间
int d = dis[u][r]; // 到达(u,p)这个状态时的最短距离d
int d = dis[u][r]; // 到达(u,r)这个状态时的最短距离d
int j = (r + 1) % k; // v节点的状态应该是u节点的状态+1后再模k
// 如果到达时间小于开放时间则将到达时间向后延长若干个k的整数倍(向上取整)

@ -166,87 +166,3 @@ int main() {
}
```
### 办法二:二分+宽搜
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202311071051699.png)
疑问与解答:
#### $Q$:为什么要建反图?
答:举个栗子,比如从$1$出发有如下路径
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 10010, M = N << 1, K = 110;
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
// 入队列的结构体
struct Node {
int id; // 节点号
int r; // %k的余数
};
int dis[N][K]; // 最短路径
int n, m, k; // n个节点m条边时间是k的整数倍即 %k=0
bool bfs(int mid) {
memset(dis, -1, sizeof dis);
queue<Node> q;
dis[n][0] = mid * k;
q.push({n, 0});
while (q.size()) {
int u = q.front().id;
int r = q.front().r;
q.pop();
if (dis[u][r] == 0) continue; // 反着走距离为0就不需要继续走了,剪枝
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i]; // 前序节点
int p = (r - 1 + k) % k; // 前序节点合法状态
if (~dis[v][p]) continue; // 如果前序点的合法状态不等于-1表示已经被宽搜走过了根据宽搜的理论就是已经取得了最短距离不需要再走
if (dis[u][r] - 1 < w[i]) continue; // dis[u][r],dis[u][r]-1,dis[u][r]-1<w[i],
// 修改合法前序点距离为当前最小距离减1
dis[v][p] = dis[u][r] - 1;
// 入队列继续扩展
q.push({v, p});
}
}
if (dis[1][0] == -1)
return false;
else
return true;
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m >> k;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(b, a, c);
}
int l = 0, r = (1e6 + 1e4) / k, ans = -1;
while (l < r) {
int mid = l + r >> 1;
if (bfs(mid)) {
ans = mid * k;
r = mid;
} else
l = mid + 1;
}
cout << ans << endl;
return 0;
}
```

Loading…
Cancel
Save