题目描述
小雨所在的城市一共有 m 条地铁线,分别标号为 1 号线,2 号线,……,m 号线。整个城市一共有 n 个车站,编号为 1∼n 。其中坐 i 号线需要花费
a
i
a_i
ai 的价格,每坐一站就需要多花费
b
i
b_i
bi的价格。i 号线有
c
i
c_i
ci个车站,而且这
c
i
c_i
ci个车站都已知,如果某一站有多条地铁线经过,则可以在这一站换乘到另一条地铁线,并且能多次换乘。现在小雨想从第 s 个车站坐地铁到第 t 个车站,地铁等待时间忽略不计,求最少花费的价格,若不能到达输出 -1 。(地铁是双向的,所以 s 可能大于 t)
输入描述:
第一行输入四个正整数 n,m,s,t,分别表示车站个数,地铁线数,起点站和终点站。
第二行到第 m + 1 行,每行前三个数为
a
i
a_i
ai,
b
i
b_i
bi,
c
i
c_i
ci, 分别表示坐 i 号线的价格,i 号线每坐一站多花的价格,i 号线车站个数。接下来
c
i
c_i
ci个数,表示 i 号线的每一个车站的编号,单调递增。
输入:
5 2 1 4
2 2 3 1 3 5
2 1 4 2 3 4 5
1
2
3
输出:
7
思路:
最开始想到了分层图,但是没有想到虚拟源点。
如果不用虚拟源点的话,就需要每层图的每个点之间相互连边,边数会很大,后来看了其他大佬的博客,发现针对不同层的同一个点,使用一个虚拟源点就可以解决这个问题。
进入虚拟源点不需要花钱,但是从虚拟源点离开就需要花钱。
在起点的时候,由于可以乘坐经过起点的任意一班地铁,故在本题中需要将起点设置为起点标号对应的虚拟源点。终点 同理。
代码:
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int,int>PII;
const int N=6e5+10,M=1e6+10;
int n,m,s,t;
int h[N], e[M], w[M],ne[M], idx; // 邻接表
bool st[N]; // 存储每个点是否在队列中
int dist[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int dijkstra(int s,int t)
{
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, s}); // first存储距离,second存储节点编号
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({dist[j], j});
}
}
}
if (dist[t] == 0x3f3f3f3f) return -1;
return dist[t];
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>m>>s>>t;
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
int t;
cin>>t;
add(t+i*n,n*m+t,0);
add(n*m+t,t+i*n,a);
for(int j=0;j<c-1;j++)
{
int k;
cin>>k;
add(t+i*n,k+i*n,b);
add(k+i*n,t+i*n,b);
add(k+i*n,n*m+k,0);
add(n*m+k,k+i*n,a);
t=k;
}
}
cout<<dijkstra(n*m+s,n*m+t);
}