P3831 [SHOI2012]回家的路 题解

题目传送门

分析

解法 1:

每两个点都连边,跑裸的最短路。

显然, n n n m m m 非常大,暴力连边会 TLE 和 MLE。

解法 2(正解):

首先,对答案产生贡献的只有换乘站,所以我们只用连两种边:

  1. 一个换乘站看作 2 2 2 个节点,连边,边权为 1 1 1(站内换乘)。
  2. x x x 相等的换乘站相连, y y y 相等的换乘站相连。

其中,起点和终点也要加入图中。

建完图后,因为起点有两个,一个是 0 0 0,一个是 m + 1 m+1 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');
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值