すぬけ君の地下鉄旅行

想一下,生活中我们是怎样乘坐地铁的?

  1. 从站厅层刷卡进站;
  2. 前往站台层乘车;
  3. 下车后从站台层走到站厅层;
  4. 从站厅层刷卡出站,此时闸机会自动从卡中扣除相应费用。

在本题中,我们模仿上述过程,每个车站都分成至少两个节点——一个站厅层和若干个站台层。从站台层前往站厅层(出站)需要支付 元费用,而从站厅层往站台层(进站)不需要花费。通过同一公司的地铁来回移动不收费。

不同地铁公司间换乘需要收费,可以看作必须“出站换乘”,即先出站到站厅层(此时付费)再从其他公司的闸机进入其他线路。

按照上述所说,我们将每个“站台”向相应的“站厅”连一条权值为 的有向边,从“站厅”到各个“站台”连一条权值为 的有向边,某条地铁线路即为一条连接两个该公司所管辖“站台”的权值为 的无向边。这样,就可以建出一个 0-1 图,进行 0-1 BFS 即可。

实际操作中显式建边会很复杂。我使用 map 存储图和 dis 数组,mp[x][c] 表示在点 乘坐 公司的线路可以直接到达的节点,dis[x][c] 表示从起点到 站的 公司站台所需的花费。特别地,我的代码中 dis[x][~0] 表示到达站厅层所需要的花费。队头为站厅时,将所有的站台从队头入队;队头是站台时,将所有连在该节点的地铁线路从队头入队,以及将站厅从队尾入队。从起点的站厅开始,到终点的站厅结束,进行 0-1 BFS 即可。

#include<bits/extc++.h>
using namespace std;
namespace pbds=__gnu_pbds;
using ui=unsigned int;
using uli=unsigned long long int;
using li=long long int;
int main(void){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    size_t n,m;cin>>n>>m;
    vector<map<ui,vector<size_t>>> mp(n);
    while (m--){
        size_t x,y;ui z;cin>>x>>y>>z;--x,--y;
        mp[x][z].push_back(y),mp[y][z].push_back(x);
    }
    deque<pair<size_t,ui>> q({{0,~0}});
    vector<map<ui,ui>> dis(n);dis[0][~0]=0;
    while (!q.empty()){
        size_t p=q.front().first;ui c=q.front().second;q.pop_front();
        ui d=dis[p][c];
        if (~c){
            if (!dis[p].count(~0)||dis[p][~0]>d+1)
                dis[p][~0]=d+1,q.emplace_back(p,~0);
            for (size_t i:mp[p][c]) if (!dis[i].count(c)||dis[i][c]>d)
                dis[i][c]=d,q.emplace_front(i,c);
        }else for (pair<ui,vector<size_t>> const& i:mp[p])
            if (!dis[p].count(i.first)||d<dis[p][i.first]) 
                dis[p][i.first]=d,q.emplace_front(p,i.first);
    }
    dis[n-1].count(~0)?cout<<dis[n-1][~0]:cout<<"-1";
    return 0;
}
posted @ 2024-03-13 00:47  MrPython  阅读(4)  评论(0)    收藏  举报  来源