题目描述:
小雨所在的城市一共有 m 条地铁线,分别标号为 1 号线,2 号线,……,m 号线。
整个城市一共有 n 个车站,编号为1∼n 。其中坐 i 号线需要花费 ai的价格,每坐一站就需要多花费 bi的价格。
i 号线有 ci个车站,而且这 ci个车站都已知,如果某一站有多条地铁线经过,则可以在这一站换乘到另一条地铁线,并且能多次换乘。
现在小雨想从第 s 个车站坐地铁到第 t 个车站,地铁等待时间忽略不计,求最少花费的价格,若不能到达输出 -1 。
(地铁是双向的,所以 s 可能大于 t)
输入描述:
第一行输入四个正整数 n,m,s,t分别表示车站个数,地铁线数,起点站和终点站。
第二行到第 m + 1 行,每行前三个数为 ai,bi,ci,分别表示坐 i 号线的价格,i 号线每坐一站多花的价格,i 号线车站个数。
接下来 ci个数,表示 i 号线的每一个车站的编号,单调递增。
输出描述:
共一行,一个数表示最小花费,若不能到达输出 -1 。
思路
最短路分层图。
每一条路线都是一层,对于每条路线重复的点,暴力的话我们考虑m^2枚举每条路线,O(n)枚举点建边
这样的复杂度显然是不允许的,所以考虑建虚点。
虚点什么意思呢,顾名思义,并不是实际存在的点,而是人为创建的不存在的点,我们考虑建立一层虚点,把每条航线的每个点都连到虚点来,连到虚点的路径权值为0,把虚点到每条路线的每个点的路径权值为a,也就是乘坐的价钱。其实可以理解为在同一站点换乘另一路线,先到虚点这一层,这一层就是车站。这样建图复杂度只需要O(nm)
然后跑一次最短路。注意起点和终点位置应该在虚点的一层。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int n, m,s,t; struct dijkstra { struct node { int to, cost; friend bool operator<(node a, node b) { return a.cost > b.cost; } }; struct Edge { int to, next; int v; } edge[1 << 21]; int head[1 << 20], tot; bool vis[1 << 20]; ll d[1 << 20]; void Init() { tot = 0; for (int i = 1; i <= 1000000; ++i) d[i] = -1, vis[i] = 0, head[i] = -1; } void add(int a, int b, int c) { edge[tot].to = b; edge[tot].v = c; edge[tot].next = head[a]; head[a] = tot++; } void dj(int x) { priority_queue<node> q; q.push({x, 0}); while (!q.empty()) { node now = q.top(); q.pop(); if (!vis[now.to]) { vis[now.to] = 1; d[now.to] = now.cost; for (int i = head[now.to]; i != -1; i = edge[i].next) { node next; next.to = edge[i].to; next.cost = now.cost + edge[i].v; if (!vis[next.to]) q.push(next); } } } } } d; int main() { d.Init();///初始化 cin>>n>>m>>s>>t; for(int i=1;i<=m;i++){ int a,b,c; cin>>a>>b>>c; int last; ///下面代码中 我的虚点建立在(m+1)*n与(m+2)*n之间 for(int j=1;j<=c;j++){ int x;cin>>x; if(j>1){///每一条路线相邻两个站点之间建边 d.add(i*n+last,i*n+x,b);///上一个点到当前点 d.add(i*n+x,i*n+last,b);///当前点到上一个点 } d.add(i*n+x,(m+1)*n+x,0);///当前点到虚点 d.add((m+1)*n+x,i*n+x,a);///虚点到当前点 last=x; } } d.dj((m+1)*n+s);//起点取虚点那一层图 ll mi=d.d[(m+1)*n+t];///终点取虚点那一层图 cout<<mi; return 0; }