NC26257 小雨坐地铁 分层图+虚拟源点

题目描述
小雨所在的城市一共有 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);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值