【AtCoder Regular Contest 061 E】Snuke's Subway Trip
最短路。
题目大意:
给定一张 $n$ 个点 $m$ 条边的无向图,每条边有一个所属的公司。
一个人连续走过同一家公司的边,只需要花费 $1$ 的代价。
问从 $1$ 走到 $n$ 的最少代价。
题解:
我们对同一家公司形成的连通块建一个虚点,每个点向这个虚点连权值为 $1$ 的边,然后跑最短路即可。
注意边权只有 $1$,所以可以直接 BFS。
由于每个连通块花费了 $2$ 的代价,所以最后答案除以 $2$ 即可。
最短路部分时间复杂度 $O(n)$。
找连通块时,每次选一条边,钦定这条边的公司,直接 dfs 找与其连通的结点即可。注意每次找的时候,不要重复经过点,否则复杂度错误。
Code:
#include<iostream>
#include<queue>
#include<map>
using namespace std;
const int N=2e6+6;
int n,m,cnt,nod;
map<int,int>head[N/10];
bool vis[N],vss[N];
struct Edge{
int to,nxt,id,c;
}e[N];
namespace graph{
struct edge{
int to,nxt;
}e[N];
int head[N],cnt,dep[N];
queue<int>q;
inline void addedge(int u,int v){
e[++cnt]=(edge){v,head[u]},head[u]=cnt;
e[++cnt]=(edge){u,head[v]},head[v]=cnt;
}
int work(){
dep[1]=1;
for(q.push(1);!q.empty();){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt)
if(!dep[e[i].to]){
dep[e[i].to]=dep[u]+1;
q.push(e[i].to);
}
}
return dep[n]?dep[n]/2:-1;
}
void clear(int u){
for(int i=head[u];i;i=e[i].nxt)vss[e[i].to]=0;
}
}
void dfs(int now,int c){
vss[now]=1;
graph::addedge(nod,now);
for(int i=head[now][c];i;i=e[i].nxt)if(!vis[e[i].id]){
vis[e[i].id]=1;
if(!vss[e[i].to])dfs(e[i].to,c);
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;nod=n;
for(int i=1;i<=m;++i){
int u,v,c;
cin>>u>>v>>c;
e[++cnt]=(Edge){v,head[u][c],i,c},head[u][c]=cnt;
e[++cnt]=(Edge){u,head[v][c],i,c},head[v][c]=cnt;
}
for(int i=1;i<=cnt;i+=2)if(!vis[e[i].id]){
++nod;
dfs(e[i].to,e[i].c);
graph::clear(nod);
}
cout<<graph::work()<<'\n';
return 0;
}