P3831 [SHOI2012]回家的路 题解
分析
解法 1:
每两个点都连边,跑裸的最短路。
显然,\(n\) 和 \(m\) 非常大,暴力连边会 TLE 和 MLE。
解法 2(正解):
首先,对答案产生贡献的只有换乘站,所以我们只用连两种边:
- 一个换乘站看作 \(2\) 个节点,连边,边权为 \(1\)(站内换乘)。
- 将 \(x\) 相等的换乘站相连,\(y\) 相等的换乘站相连。
其中,起点和终点也要加入图中。
建完图后,因为起点有两个,一个是 \(0\),一个是 \(m+1\),以这两个为源点跑最短路,再取个最小值即可。
代码
#include <bits/stdc++.h>
using namespace std;
inline int read();
inline void write(int);
int n,m;
struct poi{
int id;
int x,y;
}a[200005];
struct edge{
int to,nxt,w;
}e[1000005<<1];
int head[200005],idx;
void link(int x,int y,int w){
e[++idx]=edge{y,head[x],w};
head[x]=idx;
}
bool cmp1(poi a,poi b){
if(a.x!=b.x)return a.x<b.x;
return a.y<b.y;
}
bool cmp2(poi a,poi b){
if(a.y!=b.y)return a.y<b.y;
return a.x<b.x;
}
int ans=INT_MAX;
int dis[200005];
bool vis[200005];
void dijkstra(int s){
memset(dis,0x3f3f3f3f,sizeof dis);
memset(vis,0,sizeof vis);
priority_queue< pair<int,int>, vector<pair<int,int> >,greater<pair<int,int> > >q;
dis[s]=0;q.push(make_pair(0,s));
while (!q.empty()){
int u=q.top().second;q.pop();
if(vis[u])continue;
vis[u]=true;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
q.push(make_pair(dis[v],v));
}
}
}
}
int main(){
n=read();m=read();
for(int i=1;i<=m;i++){
a[i].x=read();a[i].y=read();a[i].id=i;
link(i,i+m+2,1);
link(i+m+2,i,1);
}
int x1=read(),y1=read(),x2=read(),y2=read();
a[0]=poi{0,x1,y1};a[m+1]=poi{m+1,x2,y2};
sort(a,a+m+2,cmp1);
for(int i=1;i<=m+1;i++){
if(a[i-1].x==a[i].x){
link(a[i-1].id,a[i].id,abs(a[i].y-a[i-1].y)*2);
link(a[i].id,a[i-1].id,abs(a[i].y-a[i-1].y)*2);
}
}
sort(a,a+m+2,cmp2);
for(int i=1;i<=m+1;i++){
if(a[i-1].y==a[i].y){
link(a[i-1].id+m+2,a[i].id+m+2,abs(a[i].x-a[i-1].x)*2);
link(a[i].id+m+2,a[i-1].id+m+2,abs(a[i-1].x-a[i].x)*2);
}
}
dijkstra(0);
ans=min(dis[m+1],dis[2*m+3]);
dijkstra(m+2);
ans=min(ans,min(dis[m+1],dis[2*m+3]));
write(ans!=0x3f3f3f3f?ans:-1);
return 0;
}
inline int read(){
register int x=0,f=0;
register char ch=getchar();
while(!isdigit(ch))f^=!(ch^45),ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
inline void write(int x){
if(x<0)putchar('-'),x=-x;
if(x>=10)write(x/10);
putchar(x%10+'0');
}