[SHOI2012]回家的路
https://www.luogu.org/problem/P3831
题意:轨道交通网络由2n条地铁线路构成,组成了一个n纵n横的交通网。如下图所示,这2n条线路每条线路都包含n个车站,而每个车站都在一组纵横线路的交汇处。只有部分车站可以进行换乘(即改变横纵移动方向),图中用方块标记。从一个车站到另一个车站需要花费2分钟,换乘需要花费1分钟,给你起点和终点,问最少需要花费多长时间。
思路:将每个换乘站拆为两个点,便分为两层,其中一层只能横向移动,另一层只能纵向移动,在换乘站可以将横纵两层用一条长度为1的边连接,然后将起点和终点向同行或者同列的换乘站连边,就可以直接跑最短路了。
#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
int n,m,tot,head[MAXN*2],x,y,x2,y2,dis[MAXN*2];
struct node1
{
int x,y,id;
}nod1[MAXN];
struct node
{
int id,w;
node(){}
node(int id,int w):id(id),w(w){}
friend bool operator < (node n1,node n2)
{
return n2.w < n1.w;
}
}nod;
priority_queue<node> qu;
struct edge
{
int v,nxt,w;
}edg[MAXN * 6];
inline void addedg(int u,int v,int w)
{
edg[tot].v = v;
edg[tot].w = w;
edg[tot].nxt = head[u];
head[u] = tot++;
}
inline bool cmp1(node1 n1,node1 n2)
{
if(n1.x == n2.x)
return n1.y < n2.y;
return n1.x < n2.x;
}
inline bool cmp2(node1 n1,node1 n2)
{
if(n1.y == n2.y)
return n1.x < n2.x;
return n1.y < n2.y;
}
inline void dijkstra(int s)
{
while(!qu.empty())
qu.pop();
memset(dis,0x3f,sizeof(dis));
dis[s] = 0;
qu.push(node(s,0));
while(!qu.empty())
{
nod = qu.top();
qu.pop();
if(dis[nod.id] != nod.w)
continue;
for(int i = head[nod.id];i != -1;i = edg[i].nxt)
{
if(dis[nod.id] + edg[i].w < dis[edg[i].v])
{
dis[edg[i].v] = dis[nod.id]+edg[i].w;
qu.push(node(edg[i].v,dis[edg[i].v]));
}
}
}
}
inline void init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
for(int i = 1;i <= m;++i)
scanf("%d%d",&nod1[i].x,&nod1[i].y),nod1[i].id = i;
scanf("%d%d%d%d",&x,&y,&x2,&y2);
sort(nod1+1,nod1+m+1,cmp1);
for(int i = 2;i <= m;++i)//同行换乘站连边
{
if(nod1[i].x == nod1[i-1].x)
{
addedg(nod1[i-1].id,nod1[i].id,(nod1[i].y-nod1[i-1].y)*2);
addedg(nod1[i].id,nod1[i-1].id,(nod1[i].y-nod1[i-1].y)*2);
}
}
sort(nod1+1,nod1+m+1,cmp2);
for(int i = 2;i <= m;++i) //同列换乘站连边
{
if(nod1[i].y == nod1[i-1].y)
{
addedg(nod1[i-1].id+m,nod1[i].id+m,(nod1[i].x-nod1[i-1].x)*2);
addedg(nod1[i].id+m,nod1[i-1].id+m,(nod1[i].x-nod1[i-1].x)*2);
}
}
for(int i = 1;i <= m;++i) //换乘站行列连边
{
addedg(nod1[i].id,nod1[i].id+m,1);
addedg(nod1[i].id+m,nod1[i].id,1);
}
for(int i = 1;i <= m;++i) //起点终点向同行连边
{
if(nod1[i].x == x)
{
addedg(2*m+1,nod1[i].id,abs(nod1[i].y-y)*2);
addedg(nod1[i].id,2*m+1,abs(nod1[i].y-y)*2);
}
if(nod1[i].x == x2)
{
addedg(2*m+2,nod1[i].id,abs(nod1[i].y-y2)*2);
addedg(nod1[i].id,2*m+2,abs(nod1[i].y-y2)*2);
}
}
for(int i = 1;i <= m;++i) //起点终点向同列连边
{
if(nod1[i].y == y)
{
addedg(2*m+1,nod1[i].id+m,abs(nod1[i].x-x)*2);
addedg(nod1[i].id+m,2*m+1,abs(nod1[i].x-x)*2);
}
if(nod1[i].y == y2)
{
addedg(2*m+2,nod1[i].id+m,abs(nod1[i].x-x2)*2);
addedg(nod1[i].id+m,2*m+2,abs(nod1[i].x-x2)*2);
}
}
dijkstra(2*m+1);
printf("%d\n",dis[2*m+2]);
}
return 0;
}