[SHOI2012]回家的路

[SHOI2012]回家的路

题意:

给定 \(2n\) 条路径,横竖交叉形成一个网格图,走一条边时不能更换边,其中有些交叉点可以更换边,消耗 \(1\),走过一条边,消耗 \(2\).

给定起点终点,询问最小值。

分析:

可以发现,我们连接所有边其实没啥用,而且还炸空间时间。

事实上,在这个图上有用的点只有:交叉点和起点终点

因为只有这些点能使变换方向,其他正着竖着走都无所谓。我们将这些点编号成 \([1,m]\) 号。

先不考虑换乘时间,因此只能考虑 \(x_i=x_j\) 或者 \(y_i=y_j\) 的情况。对于横或纵坐标相同的点,依次相连建立双向边,边权即为距离 \(\times 2\)

但是,怎么处理换乘时间呢?我们总不能说:经过这个点就 \(+1\),这样肯定是不行的。

考虑分层图:我们把整个图分成两层,将 \(i\in [1,m]\)\(i\)\(i+m\) 建立权值为 \(1\) 的双向边。

然后处理一下两层的边权,注意两层的起点终点也要建立双向边,权值为 \(0\).

代码:

//P3831 [SHOI2012]回家的路

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk make_pair
using namespace std;
const int N=1e6+5;
int n,m,s,t;
int nxt[N],ver[N],edge[N],head[N],tot;
int dis[N],vis[N];
int len(int x,int y){ return 2*abs(y-x);}
void add(int x,int y,int z){
    ver[++tot]=y; nxt[tot]=head[x]; head[x]=tot; edge[tot]=z;
}

struct node{
    int x,y,id;
}a1[N],a2[N];

bool cmp1(node a,node b){
    return a.x==b.x?a.y<b.y:a.x<b.x;
}
bool cmp2(node a,node b){
    return a.y==b.y?a.x<b.x:a.y<b.y;
}
priority_queue<pii> q;
void dijkstra(int s){
    memset(dis,0x3f3f3f3f,sizeof(dis));
    dis[s]=0;
    q.push(mk(0,s));
    while(!q.empty()){
        int x=q.top().second; q.pop();
        if(vis[x]) continue;
        vis[x]=1;
        for(int i=head[x];i;i=nxt[i]){
            int y=ver[i],z=edge[i];
            if(dis[y]>dis[x]+z){
                dis[y]=dis[x]+z; q.push(mk(-dis[y],y));
            }
        }
    }
}

int main(){
    cin>>n>>m; m+=2;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&a1[i].x,&a1[i].y); a2[i].x=a1[i].x,a2[i].y=a1[i].y;
        a1[i].id=i,a2[i].id=i+m;
        if(i==m-1) s=i; if(i==m) t=i;
    }
    sort(a1+1,a1+1+m,cmp1); 
    sort(a2+1,a2+m+1,cmp2);
    for(int i=1;i<m;i++){
        if(a1[i].x!=a1[i+1].x) continue;
        add(a1[i].id,a1[i+1].id,len(a1[i+1].y,a1[i].y));
        add(a1[i+1].id,a1[i].id,len(a1[i+1].y,a1[i].y));
    }
    for(int i=1;i<m;i++){
        if(a2[i].y!=a2[i+1].y) continue;
        add(a2[i].id,a2[i+1].id,len(a2[i+1].x,a2[i].x));
        add(a2[i+1].id,a2[i].id,len(a2[i+1].x,a2[i].x));
    }
    for(int i=1;i<=m-2;i++){
        add(i,i+m,1); add(i+m,i,1);
    }
    add(s,s+m,0); add(s+m,s,0); add(t,t+m,0); add(t+m,t,0);
    dijkstra(s);
    if(dis[t]==0x3f3f3f3f) puts("-1");
    printf("%d\n",dis[t]);
    system("pause");
    return 0;
}
posted @ 2021-11-02 21:10  Evitagen  阅读(254)  评论(0编辑  收藏  举报