题目描述:
小雨所在的城市一共有 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;
}
京公网安备 11010502036488号