思路、证明什么的都在代码的注释里。
需要说明的一点是STL堆默认为大顶堆,一定要记住。。。。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=200005<<1;
typedef pair<int,int> node;
struct edge
{
int to,next;
long long val;
}e[maxn<<1];
struct point
{
int x,y,id;
}p[maxn];
int n,cnt;
int head[maxn];
bool vst[maxn];
long long dist[maxn];
void insert(int a,int b,long long val)
{
e[++cnt].val=val;e[cnt].to=b;e[cnt].next=head[a];head[a]=cnt;
}
bool cmp1(point a,point b)
{
return a.x<b.x;
}
bool cmp2(point a,point b)
{
return a.y<b.y;
}
void dijkstra()
{
priority_queue< node,vector<node>,greater<node> >q;
memset(dist,0x3f,sizeof dist);
dist[1]=0;
q.push((node){0,1});
while(!q.empty())
{
int u=q.top().second;
q.pop();
if(vst[u])continue;
vst[u]=true;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(dist[v]>dist[u]+e[i].val)
{
dist[v]=dist[u]+e[i].val;
q.push((node){dist[v],v});
}
}
}
printf("%lld",dist[n]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
p[i].id=i;
}
//假设他们相邻,则两点A、B之间连边一定是最优解
//假设他们y方向相邻,x方向上有一中间点C,那么可以确定如果A,B通过x方向得到的答案必不差于经过C
//假设通过y方向直接连比经过C优,这是可以做到的,然而y方向上A,B两点已连,所以如此连边可以保证答案正确
//假设A,B y方向上中间还有D点间隔,那么A,B无法连边,现在考虑A,B无法连边对答案的影响
//假设A,B直接连严格更优,那么 必然是通过x,y其中一个方向实现的,然而可以确定这两个方向绕过C或D必不比直接连边更差
//由此假设不成立,所以A,B不连边对答案无影响,由此保证了答案的正确性
sort(p+1,p+n+1,cmp1);
for(int i=2;i<=n;i++)
{
insert(p[i].id,p[i-1].id,p[i].x-p[i-1].x);
insert(p[i-1].id,p[i].id,p[i].x-p[i-1].x);
//因为不知要向那个方向,所以双向连边,至少要用到一条
}
sort(p+1,p+n+1,cmp2);
for(int i=2;i<=n;i++)
{
insert(p[i].id,p[i-1].id,p[i].y-p[i-1].y);
insert(p[i-1].id,p[i].id,p[i].y-p[i-1].y);
}
dijkstra();
return 0;
}