题目大意:
题目链接:
JZOJ:https://jzoj.net/senior/#main/show/3086
洛谷:https://www.luogu.org/problemnew/show/P3831
m
o
r
e
D
moreD
moreD城的城市轨道交通建设终于全部竣工,由于前期规划周密,建成后的轨道交通网络由
2
n
2n
2n条地铁线路构成,组成了一个
n
n
n纵
n
n
n横的交通网。如下图所示,这
2
n
2n
2n条线路每条线路都包含
n
n
n个车站,而每个车站都在一组纵横线路的交汇处。
出于建设成本的考虑,并非每个车站都能够进行站内换乘,能够进行站内换乘的地铁站共有
m
m
m 个,在下图中,标上方块标记的车站为换乘车站。已知地铁运行
1
1
1站需要
2
2
2分钟,而站内换乘需要步行
1
1
1分钟。 你的最后一个作业就是算出,在不中途出站的前提下,从学校回家最快需要多少时间(等车时间忽略不计)。
思路:
每个点拆成横点和竖点,之间连一条长度为
1
1
1的边作为转站时间。
若两个点在同一行或同一列,之间连上长度的
2
2
2倍的边。
跑最短路即可。注意连边时需要排序连边,保证复杂度。
时间复杂度:
O
(
n
l
o
g
n
)
O(n\ logn)
O(n logn)
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define mp make_pair
using namespace std;
const int N=200010;
int n,m,S,T,tot,head[N],dis[N],id[N];
bool vis[N];
priority_queue<pair<int,int> > q;
struct edge
{
int to,dis,next;
}e[N*4];
struct station
{
int x,y;
}a[N];
void add(int from,int to,int dis)
{
e[++tot].to=to;
e[tot].dis=dis;
e[tot].next=head[from];
head[from]=tot;
}
bool cmp1(int x,int y)
{
return a[x].x<a[y].x||(a[x].x==a[y].x&&a[x].y<a[y].y);
}
bool cmp2(int x,int y)
{
return a[x].y<a[y].y||(a[x].y==a[y].y&&a[x].x<a[y].x);
}
void dij()
{
memset(dis,0x3f3f3f3f,sizeof(dis));
q.push(mp(0,S));
dis[S]=0;
vis[S]=1;
while (q.size())
{
int u=q.top().second;
q.pop();
for (int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
if (dis[v]>dis[u]+e[i].dis)
{
dis[v]=dis[u]+e[i].dis;
if (!vis[v])
{
q.push(mp(-dis[v],v));
vis[v]=1;
}
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
m+=2;
for (int i=1;i<=m-2;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
id[i]=i;
}
S=m-1; T=m;
scanf("%d%d",&a[S].x,&a[S].y); id[S]=S;
scanf("%d%d",&a[T].x,&a[T].y); id[T]=T;
sort(id+1,id+1+m,cmp1);
for (int i=1;i<m;i++)
if (a[id[i]].x==a[id[i+1]].x)
{
add(id[i],id[i+1],(a[id[i+1]].y-a[id[i]].y)*2);
add(id[i+1],id[i],(a[id[i+1]].y-a[id[i]].y)*2);
}
sort(id+1,id+1+m,cmp2);
for (int i=1;i<m;i++)
if (a[id[i]].y==a[id[i+1]].y)
{
add(id[i]+m,id[i+1]+m,(a[id[i+1]].x-a[id[i]].x)*2);
add(id[i+1]+m,id[i]+m,(a[id[i+1]].x-a[id[i]].x)*2);
}
add(T,T+m,0); add(T+m,T,0);
add(S,S+m,0); add(S+m,S,0);
for (int i=1;i<=m-2;i++)
add(i,i+m,1),add(i+m,i,1);
dij();
if (min(dis[T],dis[T+m])<1e9) printf("%d\n",min(dis[T],dis[T+m]));
else printf("-1\n");
return 0;
}