题目链接:[SHOI2012]回家的路
每次到换乘点有两种决策。换乘或者是不换。显然可以dp或者分层图。
分层图比较好写。
我们直接按照横的路建边作为第一层,竖的路建边作为第二层。换乘点可以花费1横竖相通。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e5+10,M=2e6+10;
int n,m,cnt,base,vis[N],d[N];
int head[N],nex[M],to[M],w[M],tot;
struct node{int x,y,id;}t[N];
int cmpx(node a,node b){return a.x==b.x?a.y<b.y:a.x<b.x;}
int cmpy(node a,node b){return a.y==b.y?a.x<b.x:a.y<b.y;}
inline void ade(int a,int b,int c){
to[++tot]=b; nex[tot]=head[a]; w[tot]=c; head[a]=tot;
}
inline void add(int a,int b,int c){ade(a,b,c); ade(b,a,c);}
int Dijkstra(int s,int t){
priority_queue<pair<int,int> > q; memset(d,0x3f,sizeof d);
d[s]=0; q.push({0,s});
while(q.size()){
int u=q.top().second; q.pop();
if(u==t) return d[t];
if(vis[u]) continue; vis[u]=1;
for(int i=head[u];i;i=nex[i]){
if(d[to[i]]>d[u]+w[i]){
d[to[i]]=d[u]+w[i]; q.push({-d[to[i]],to[i]});
}
}
}
return d[t];
}
signed main(){
cin>>n>>m; base=m+2;
for(int i=1;i<=m+2;i++){
scanf("%d %d",&t[i].x,&t[i].y); t[i].id=i;
if(i<=m) add(i,i+base,1);
}
sort(t+1,t+3+m,cmpx);
for(int i=2;i<=m+2;i++) if(t[i].x==t[i-1].x)
add(t[i-1].id+base,t[i].id+base,2*abs(t[i-1].y-t[i].y));
sort(t+1,t+3+m,cmpy);
for(int i=2;i<=m+2;i++) if(t[i].y==t[i-1].y)
add(t[i-1].id,t[i].id,2*abs(t[i-1].x-t[i].x));
add(m+1,m+1+base,0),add(base,base*2,0);
int res=Dijkstra(m+1,m+2);
if(res==0x3f3f3f3f) res=-1;
cout<<res;
return 0;
}