【bzoj 4152】 The Captain 【AMPPZ2014】

Description

给定平面上的n个点,定义(x1,y1)到(x2,y2)的费用为min(|x1-x2|,|y1-y2|),求从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

对于这道题,由于(x_i-x_k)+(x_k-x_j)=x_i-x_j所以对于任意一个点,只需要将它与和它横坐标或纵坐标相邻的点连边,然后跑一遍最短路即可,下面是程序:

dijkstra+堆优化:

#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
const int N=200005;
struct node{
	int x;
	ll w;
	bool operator <(const node &p)const{
		return w>p.w;
	}
};
struct Point{
	int x,y,i;
}a[N];
struct edge{
	int v,next;
	ll w;
}e[N<<2];
ll dis[N];
int head[N],k;
bool vis[N];
priority_queue<node>q;
bool cmp1(Point a,Point b){
	return a.x<b.x;
}
bool cmp2(Point a,Point b){
	return a.y<b.y;
}
void add(int u,int v,ll w){
	e[++k]=(edge){v,head[u],w};
	head[u]=k;
}
void dijkstra(){
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	dis[1]=0;
	q.push((node){1,0});
	int i,x;
	while(!q.empty()){
		x=q.top().x;
		q.pop();
		if(vis[x]){
			continue;
		}
		vis[x]=1;
		for(i=head[x];i!=-1;i=e[i].next){
			if(dis[x]+e[i].w<dis[e[i].v]){
				dis[e[i].v]=dis[x]+e[i].w;
				q.push((node){e[i].v,dis[e[i].v]});
			}
		}
	}
}
int main(){
	int n,i;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d%d",&a[i].x,&a[i].y);
		a[i].i=i;
	}
	memset(head,-1,sizeof(head));
	sort(a+1,a+n+1,cmp1);
	for(i=1;i<n;i++){
		add(a[i].i,a[i+1].i,a[i+1].x-a[i].x);
		add(a[i+1].i,a[i].i,a[i+1].x-a[i].x);
	}
	sort(a+1,a+n+1,cmp2);
	for(i=1;i<n;i++){
		add(a[i].i,a[i+1].i,a[i+1].y-a[i].y);
		add(a[i+1].i,a[i].i,a[i+1].y-a[i].y);
	}
	dijkstra();
	printf("%lld\n",dis[n]);
	return 0;
}

SPFA(这题卡SPFA,但加上SLF和网上的玄妙优化(入队多的放队首)可以卡过):

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
const int N=200005;
ll dis[N];
int sum[N];
struct queue{
	int l,r,a[N];
	void clear(){
		l=0,r=1;
	}
	bool empty(){
		return l+1==r;
	}
	void push(int x){
		if(dis[x]<dis[a[(l+1)%N]]||sum[x]>sum[a[(l+1)%N]]){
			a[l]=x;
			l=(l+N-1)%N;
		}
		else{
			a[r]=x;
			r=(r+1)%N;
		}
		++sum[x];
	}
	void pop(){
		l=(l+1)%N;
	}
	int front(){
		return a[(l+1)%N];
	}
}q;
struct Point{
	int x,y,i;
}a[N];
struct edge{
	int v,next;
	ll w;
}e[N<<2];
int head[N],k;
bool vis[N];
bool cmp1(Point a,Point b){
	return a.x<b.x;
}
bool cmp2(Point a,Point b){
	return a.y<b.y;
}
void add(int u,int v,ll w){
	e[++k]=(edge){v,head[u],w};
	head[u]=k;
}
void SPFA(){
	memset(dis,0x3f,sizeof(dis));
	q.clear();
	dis[1]=0;
	q.push(1);
	vis[1]=1;
	int u,v,i;
	while(!q.empty()){
		u=q.front();
		q.pop();
		vis[u]=0;
		for(i=head[u];i!=-1;i=e[i].next){
			v=e[i].v;
			if(dis[u]+e[i].w<dis[v]){
				dis[v]=dis[u]+e[i].w;
				if(!vis[v]){
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
}
int main(){
	int n,i;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d%d",&a[i].x,&a[i].y);
		a[i].i=i;
	}
	memset(head,-1,sizeof(head));
	sort(a+1,a+n+1,cmp1);
	for(i=1;i<n;i++){
		add(a[i].i,a[i+1].i,a[i+1].x-a[i].x);
		add(a[i+1].i,a[i].i,a[i+1].x-a[i].x);
	}
	sort(a+1,a+n+1,cmp2);
	for(i=1;i<n;i++){
		add(a[i].i,a[i+1].i,a[i+1].y-a[i].y);
		add(a[i+1].i,a[i].i,a[i+1].y-a[i].y);
	}
	SPFA();
	printf("%lld\n",dis[n]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值