分层图+最短路
ps:刚刚开始看这个题目的时候,看完一遍不理解,再看一遍还是不理解,于是冷静一波~发现这个题目真的很复杂!!!
题目是求最短路,最短路可以参考我的另外一篇博客快速理解最短路径算法
对于这个题目而言,我们需要去做出一个分层图出来,因为地铁线之间有交叉,所以在存储完地铁线之后,我们需要对建立一个超级源点,并且规定,从地铁站到超级源点需要不需要花费,而从超级源点到地铁站需要花费,这就相当于一个中转站一样,我们通过中转站去下一个地铁当然需要花费,超级源点相当于一条虚拟的地铁站,该线上有所有的地铁站虚点。
分层图就是一层一层的,这样我们就可以知道当前节点的位置坐标,就相当于一个二维数组,不同的是这个二维数组存在三维空间中
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int inf = 0x3f3f3f3f; int cnt, vis[maxn], dis[maxn], head[maxn], a[maxn], b[maxn]; struct node //结构体存储边的信息,起点,终点,边权,下一个节点 { int v, w, next; } e[maxn]; void add(int u, int v, int w) //链式前向星存图,每次加边都放在头结点的后面,降低时间复杂度 { e[++cnt].v = v; e[cnt].w = w; e[cnt].next = head[u]; head[u] = cnt; } queue<int> q; void spfa(int x) { memset(dis, inf, sizeof(dis)); //初始化 memset(vis, 0, sizeof(vis)); vis[x] = 1; //标记该节点已经使用 dis[x] = 0; //该节点到自己的距离为0 q.push(x); //放入队列 while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; //出队 for (int i = head[u]; ~i; i = e[i].next) //遍历该节点所有的邻接点 { int v = e[i].v; //后继节点 if (dis[v] > dis[u] + e[i].w) //松弛操作 { dis[v] = dis[u] + e[i].w; if (!vis[v]) //判断该节点是否遍历过 { vis[v] = 1; q.push(v); } } } } } int main() { int n, m, s, t, sum, x, last; cnt = 0; scanf("%d%d%d%d", &n, &m, &s, &t); memset(head, -1, sizeof(head)); for (int i = 1; i <= m; ++i) { scanf("%d%d%d", &a[i], &b[i], &sum); for (int j = 1; j <= sum; ++j) { scanf("%d", &x); if (j != 1) //第一个地铁站因为前面为空,所以直接和超级源点相连接,其余节点不仅要和超级源点链接,还要和上一个节点连接 { add((i - 1) * n + x, (i - 1) * n + last, b[i]); //加边操作从当前节点到上一个节点需要花费b[i] add((i - 1) * n + last, (i - 1) * n + x, b[i]); //由于是无向图,所有从上一个节点到当前节点需要的花费也是b[i] } add((i - 1) * n + x, n * m + x, 0); //地铁站到超级源点不需要花费 add(n * m + x, (i - 1) * n + x, a[i]); //超级源点到地铁站需要花费a last = x; //储存上一个地铁站 } } spfa(n * m + s); //开始跑spfa if (dis[n * m + t] == inf) printf("-1\n"); else printf("%d\n", dis[n * m + t]); return 0; }