正题
luogu评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P3831
题目大意
有n∗nn*nn∗n的铁路网走一格代价为2,mmm个中转站可以改变方向代价为1。求两个点之间的最短路。
解题思路
我们发现n∗nn*nn∗n很大,所以我们考虑根据mmm建图。算上起点和终点为中转站。
对于每个中转站,我们只连接上下左右最近的点,这个排序可以做到。这样边数就不会太多。可是如何解决转向的问题。
对于每个点,拆成横点和纵点,横着的连横点,竖着的连纵点,横点和纵点之间建立一条边,长度为1。
问题完美解决
codecodecode
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#define py(aaa) aaa*2-1
#define px(aaa) aaa*2
using namespace std;
const int N=200100,L=20010;
struct node{int to,next,w;
}a[N*5];
vector<int> in_x[L],in_y[L];
queue<int> q;
int ls[N],tot,n,m,f[N],v[N],x[N],y[N];
bool cmp_x(int xs,int ys)
{return y[xs]<y[ys];}
bool cmp_y(int xs,int ys)
{return x[xs]<x[ys];}
void addl(int x,int y,int w)
{a[++tot].to=y;a[tot].w=w;a[tot].next=ls[x];ls[x]=tot;
}
int spfa(int s,int t)
{memset(f,0x3f,sizeof(f));f[py(s)]=f[px(s)]=0;q.push(py(s));q.push(px(s));while(!q.empty()){int x=q.front();q.pop();for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(f[x]+a[i].w<f[y]){f[y]=f[x]+a[i].w;if(!v[y]){q.push(y);v[y]=true;}}}v[x]=false;}if(f[px(t)]>=1061109567) return -1;return min(f[px(t)],f[py(t)]);
}
int main()
{scanf("%d%d",&n,&m);m+=2;for(int i=1;i<=m;i++){scanf("%d%d",&x[i],&y[i]);in_x[x[i]].push_back(i);in_y[y[i]].push_back(i);addl(px(i),py(i),1);addl(py(i),px(i),1);}for(int i=1;i<=n;i++)sort(in_x[i].begin(),in_x[i].end(),cmp_x);for(int k=1;k<=n;k++)for(int i=0;i+1<in_x[k].size();i++){int as=in_x[k][i],bs=in_x[k][i+1];addl(px(as),px(bs),(y[bs]-y[as])*2);addl(px(bs),px(as),(y[bs]-y[as])*2);}for(int i=1;i<=n;i++)sort(in_y[i].begin(),in_y[i].end(),cmp_y);for(int k=1;k<=n;k++)for(int i=0;i+1<in_y[k].size();i++){int as=in_y[k][i],bs=in_y[k][i+1];addl(py(as),py(bs),(x[bs]-x[as])*2);addl(py(bs),py(as),(x[bs]-x[as])*2);}printf("%d",spfa(m-1,m));
}