题目链接
https://codeforces.com/contest/1422/problem/D
题意
n*n网格,m个特殊点,和特殊点x或y坐标相同的点可以直接传送到特殊点,人物移动只能上下左右,给出起始点终止点坐标,保证起始点终止点不是特殊点,求最短距离
思路
最短路,难点在于建图。思路和HDU - 4725 相当接近。
我们无法处理m²级别的边,但每一个特殊点可以和同一行或者同一列的点传送,那么我们可以为每一个节点建立一个x轴对应点和一个y轴对应点。1e5的点处理后最多变成3e5。
我们将特殊点和对应点连边,双向边【题意中的确说了只能从别的点传送到特殊点,但为了保证特殊点可以向别的特殊点传送,必须是双向边,可以考虑特殊点(5,3)(5,2)(4,2)的情况,如果是单向边无法保证这三个点间可以无花费互相到达】,权为0,这样就实现了特殊点传送的情况。
注意,我们需要保证x和y轴互相是联通的,x和y也是1e5级别的点,无法通过两次for造成强联通。但xy轴已经降维到了一维的情况了,比如A(1),B(2),C(3)的情况,A-C的距离其实就是A-B-C的距离,在仅计算最短路时,A-C的边是毫无意义的。所以我们可以排序x和y,从小到大依次链接左右即可,这样可以保证xy轴内部的联通。
对于起点,可行的移动方案是走向任何一个特殊点或者终点;对于走向终点,直接连边即可。对于走向特殊点,显然,min(dis(s-x对应点),dis(s-y对应点))小于等于s直接前往特殊点。所以直接分别向特殊点连边即可。回到起点无意义,用单向边连接可以优化效率。
对于终点,由于终点不是特殊点,所以到达终点仅可能是起点直达或者特殊点直达,直接遍历向t连接单向边即可
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=1000500;//开大小心RE
const int maxe=2000500;
const int inf=0x3f3f3f3f;
int n,m;
int cnt;
int head[maxn];
struct Edge{
int to,w,next;
}edge[maxe];
void init(){
memset(head,-1,sizeof head);
cnt=0;
}
void add(int u,int v,int w){
edge[cnt].to=v,edge[cnt].w=w;
edge[cnt].next=head[u],head[u]=cnt++;
}
struct Node{
int x,y,id;
}node[maxn];//存放特殊点坐标
int sx,sy,tx,ty;//始终点
bool cmpwithx(Node p1,Node p2){
return p1.x<p2.x;
}
bool cmpwithy(Node p1,Node p2){
return p1.y<p2.y;
}
typedef pair<int,int> P;
int dis[maxn];
void dij(int start){
memset(dis,0x3f,sizeof(dis));
priority_queue<P,vector<P>,greater<P> > q;
dis[start]=0;
q.push(P(0,start));
while(!q.empty()){
P p=q.top(); q.pop();
int u=p.second;
if(dis[u]<p.first) continue;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].w){
dis[v]=dis[u]+edge[i].w;
q.push(P(dis[v],v));
}
}
}
return ;
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("D:\\code\\IO\\in.txt","r",stdin);
freopen("D:\\code\\IO\\out.txt","w",stdout);
#endif
init();
cin>>n>>m;
int s=3*m+1,t=s+1;//1--m为特殊点,m+1--2m为x轴点,2m+1--3m为y轴点
cin>>sx>>sy>>tx>>ty;
for(int i=1;i<=m;i++){
cin>>node[i].x>>node[i].y;
node[i].id=i;
add(i+m,i,0);add(i,i+m,0);//特殊点向xy连边,注意是双向边
add(i+2*m,i,0);add(i,i+2*m,0);
}
sort(node+1,node+1+m,cmpwithx);
for(int i=1;i<m;i++){//保证x轴联通
add(node[i].id+m,node[i+1].id+m,abs(node[i].x-node[i+1].x));
add(node[i+1].id+m,node[i].id+m,abs(node[i].x-node[i+1].x));
}
sort(node+1,node+1+m,cmpwithy);
for(int i=1;i<m;i++){//保证y轴联通
add(node[i].id+m+m,node[i+1].id+m+m,abs(node[i].y-node[i+1].y));
add(node[i+1].id+m+m,node[i].id+m+m,abs(node[i].y-node[i+1].y));
}
for(int i=1;i<=m;i++){//起点向所有xy轴点连边
add(s,node[i].id+m,abs(node[i].x-sx));
add(s,node[i].id+m+m,abs(node[i].y-sy));
}
for(int i=1;i<=m;i++){//终点连接所有特殊点和起点
add(node[i].id,t,abs(tx-node[i].x)+abs(ty-node[i].y));
}
add(s,t,abs(sx-tx)+abs(sy-ty));
dij(s);
cout<<dis[t]<<endl;
//system("pause");
return 0;
}