[AMPPZ2014]The Captain 解题报告

传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4152

Description

给定平面上的n个点,定义(x_1,y_1)(x_2,y_2)的费用为min(|x_1-x_2|,|y_1-y_2|),求从1号点走到n号点的最小费用。

Input

第一行包含一个正整数n(2<=n<=200000),表示点数。

接下来n行,每行包含两个整数x_i,y_i(0<=x_i,y_i<=10^9),依次表示每个点的坐标。

Output

一个整数,即最小费用。

Sample Input

5
2 2
1 1
4 5
7 1
6 7

Sample Output

2

 

如果暴力建图,连n^2条边,是会爆内存与时间的。

我们容易发现一个性质:我们先忽略掉坐标的其中一维,比如只考虑横坐标时,把点按横坐标排好序后,1\rightarrow i的边等价于1\rightarrow 2\rightarrow 3\rightarrow ...\rightarrow i的边,因为1\rightarrow i的边权为x_i-x_1,而1\rightarrow 2\rightarrow 3\rightarrow ...i的总边权为(x_2-x_1)+(x_3-x_2)+...+(x_i-x_{i-1})=x_i-x_1,故为等价的,故我们只需要以一条链串过去,就可以表示所有的边,同时减少多余的边了;

所以我们以x为关键字先排一遍序,相邻点之间建一条边权为横坐标之差的边;再以y为关键字排一遍序,相邻点之间建一条边权为纵坐标之差的边。因为跑最短路算法取的是更小的一条边,所以每两个点之间的两条边(一条以x算,一条以y算),也能等价于题目上说的min(|x_1-x_2|,|y_1-y_2|),就建好了题目要求的图。

由于n较大,需要用用堆优化的dijkstraspfa(但是听说spfa会被卡?)

代码:(我是手写堆的铁头娃)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
	struct forward_star
	{
		int next,to;
		long long w;
	};
	struct coordinate
	{
		int x,y,label;
	};
	int n,cnt,tot;
	forward_star edge[800001];
	int head[200001];
	long long dist[200001];
	int heap[200001];
	int ref[200001];
	bool vis[200001];
	bool in[200001];
	coordinate point[200001];
bool cmp1(coordinate i,coordinate j)
{
	return (i.x<j.x);
}
bool cmp2(coordinate i,coordinate j)
{
	return (i.y<j.y);
}
void add(int u,int v,int w)
{
	cnt++;
	edge[cnt].to=v;
	edge[cnt].next=head[u];
	edge[cnt].w=w;
	head[u]=cnt;
}
void up(int now)
{
	while (now>1&&dist[heap[now]]<dist[heap[now/2]])
	{
		swap(heap[now],heap[now/2]);
		swap(ref[heap[now]],ref[heap[now/2]]);
		now/=2;
	}
}
void down(int now)
{
	while (now*2<=tot)
	{
		if (now*2+1<=tot)
		{
			int k;
			if (dist[heap[now*2]]<dist[heap[now*2+1]]) k=now*2; else k=now*2+1;
			if (dist[heap[now]]>dist[heap[k]])
			{
				swap(heap[now],heap[k]);
				swap(ref[heap[now]],ref[heap[k]]);
				now=k;
			}
			else break;
		}
		else
		{
			if (dist[heap[now]]>dist[heap[now*2]])
			{
				swap(heap[now],heap[now*2]);
				swap(ref[heap[now]],ref[heap[now*2]]);
				now*=2;
			}
			else break;
		}
	}
}
void adjust()
{
	heap[1]=heap[tot];
	ref[heap[1]]=1;
	tot--;
	down(1);
}
void addheap(int i)
{
	heap[++tot]=i;
	ref[i]=tot;
	up(tot);
}
void dijkstra()
{
	for (int i=1;i<=n;i++)
		dist[i]=1000000000000000000ll;
	dist[1]=0;
	in[1]=true;
	addheap(1);
	while (tot>0)
	{
		int now=heap[1];
		adjust();
		vis[now]=true;
		int j=head[now];
		while (j!=0)
		{
			if (dist[now]+edge[j].w<dist[edge[j].to])
			{
				dist[edge[j].to]=dist[now]+edge[j].w;
				if (!in[edge[j].to])
				{
					in[edge[j].to]=true;
					addheap(edge[j].to);
				} else if (!vis[edge[j].to]) up(ref[edge[j].to]);
			}
			j=edge[j].next;
		}
	}
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d%d",&point[i].x,&point[i].y);
		point[i].label=i;
	}
	sort(point+1,point+n+1,cmp1);
	for (int i=1;i<n;i++)
	{
		add(point[i].label,point[i+1].label,point[i+1].x-point[i].x);
		add(point[i+1].label,point[i].label,point[i+1].x-point[i].x);
	}
	sort(point+1,point+n+1,cmp2);
	for (int i=1;i<n;i++)
	{
		add(point[i].label,point[i+1].label,point[i+1].y-point[i].y);
		add(point[i+1].label,point[i].label,point[i+1].y-point[i].y);
	}
	dijkstra();
	printf("%lld",dist[n]);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值