Rudolf and Subway
同:[ARC061E] すぬけ君の地下鉄旅行。水两篇题解真香
想一下,生活中我们是怎样乘坐地铁的?
- 从站厅层刷卡进站;
- 前往站台层乘车;
- 下车后从站台层走到站厅层;
- 从站厅层刷卡出站,此时闸机会自动从卡中扣除相应费用。
我们将乘坐一轮地铁看作花费 元(换乘必须先付费出站),使总花费最小。这个问题与原题是等价的。
我们每个车站都分成至少两个节点——一个站厅层和若干个站台层。从站台层前往站厅层(出站)需要支付 元费用,而从站厅层往站台层(进站)不需要花费。通过同一条地铁来回移动不收费。想要换乘其他线路,必须先付费出站到站厅层,再进入其他线路的站台。
按照上述所说,我们将每个“站台”向相应的“站厅”连一条权值为 的有向边,从“站厅”到各个“站台”连一条权值为 的有向边,某条地铁线路即为一条连接两个该线路所属“站台”的权值为 的无向边。这样,就可以建出一个 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 T;cin>>T;
while (T--){
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);
}
size_t s,t;cin>>s>>t;--s,--t;
deque<pair<size_t,ui>> q({{s,~0}});
vector<map<ui,ui>> dis(n);dis[s][~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[t].count(~0)?cout<<dis[t][~0]:cout<<"-1")<<'\n';
}
return 0;
}