分层图+最短路
小雨坐地铁
-
分层图+最短路 - 题目分析
题目分析
题目传送门——小雨坐地铁
ps:刚刚开始看这个题目的时候,看完一遍不理解,再看一遍还是不理解,于是冷静一波~发现这个题目真的很复杂!!!
题目是求最短路,最短路可以参考我的另外一篇博客快速理解最短路径算法
对于这个题目而言,我们需要去做出一个分层图出来,因为地铁线之间有交叉,所以在存储完地铁线之后,我们需要对建立一个超级源点,并且规定,从地铁站到超级源点需要不需要花费,而从超级源点到地铁站需要花费,这就相当于一个中转站一样,我们通过中转站去下一个地铁当然需要花费,超级源点相当于一条虚拟的地铁站,该线上有所有的地铁站虚点。
分层图就是一层一层的,这样我们就可以知道当前节点的位置坐标,就相当于一个二维数组,不同的是这个二维数组存在三维空间中
spfa+链式前向星
spfa算法由于也是用队列实现的,所以它的代码和BFS代码很像~~
#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;
}
dijkstra+链式前向星
#include <bits/stdc++.h>
#include <queue>
using namespace std;
const int maxn = 1100;
int head[maxn * maxn], cnt, m, n, dis[maxn * maxn];
struct node
{
bool operator()(int x, int y)
{
return dis[x] > dis[y];
}
};
priority_queue<int, vector<int>, node> q;
struct edge
{
int nx, to, w;
} edge[maxn * maxn * 2];
void add(int u, int v, int w)
{
edge[++cnt].nx = head[u];
edge[cnt].to = v;
edge[cnt].w = w;
head[u] = cnt;
}
void dijkstra(int s) //最短路算法
{
dis[s] = 0;
q.push(s);
while (!q.empty())
{
int u = q.top();
q.pop();
for (int i = head[u]; ~i; i = edge[i].nx)
{
int v = edge[i].to;
if (dis[v] > dis[u] + edge[i].w)
{
dis[v] = dis[u] + edge[i].w;
q.push(v);
}
}
}
}
int main()
{
memset(dis, 0x3f3f3f3f, sizeof(dis));
memset(head, -1, sizeof(head));
int s, t;
cin >> n >> m >> s >> t;
for (int i = 1; i <= m; i++)
{
int a, b, c;
cin >> a >> b >> c;
int last = -1; //记录上一站
for (int j = 1; j <= c; j++)
{
int x;
cin >> x;
if (j != 1)
{
add((i - 1) * n + x, (i - 1) * n + last, b); //站与站之间花费b
add((i - 1) * n + last, (i - 1) * n + x, b);
}
add((i - 1) * n + x, m * n + x, 0); //每一层到最后一层花费为0
add(m * n + x, (i - 1) * n + x, a); //最后一层去上面层花费a
last = x;
}
}
dijkstra(s + m * n);
if (dis[t + m * n] >= 0x3f3f3f3f)
cout << -1;
else
cout << dis[t + m * n];
return 0;
}
方法二:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> ppi;
const int maxn = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int head[maxn], dis[maxn], cnt = 0;
int a[maxn], b[maxn];
struct node
{
int to, w, next;
} e[maxn];
void add(int u, int v, int w)
{
e[++cnt].to = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
priority_queue<ppi, vector<ppi>, greater<ppi> > q;
void dijkstra(int s)
{
memset(dis, inf, sizeof(dis));
dis[s] = 0;
q.push(ppi(0, s));
while (!q.empty())
{
int a = q.top().first;
int b = q.top().second;
q.pop();
if (a > dis[b])
continue;
for (int i = head[b]; ~i; i = e[i].next)
{
int v = e[i].to;
if (dis[v] > dis[b] + e[i].w)
{
dis[v] = dis[b] + e[i].w;
q.push(ppi(dis[v], v));
}
}
}
}
int main()
{
int n, m, s, t, sum, x, last;
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 * n + x, i * n + last, b[i]);
add(i * n + last, i * n + x, b[i]);
}
add(i * n + x, x, 0);
add(x, i * n + x, a[i]);
last = x;
}
}
dijkstra(s);
if (dis[t] != inf)
printf("%d\n", dis[t]);
else
printf("-1\n");
}
一个人最好的生活状态,是该看书时看书,该玩时尽情玩,看见优秀的人欣赏,看到落魄的人也不轻视,有自己的小生活和小情趣,不用去想改变世界,努力去活出自己。没人爱时专注自己,有人爱时,有能力拥抱彼此。 |
---|