bzoj4152(神奇的最短路)

经验

1:memset不能把数组赋值成1000000这样类似的数,虽然不报错,但是却不会赋值成功,更需要注意!

2:utility,priority_queue,pair等常用的stl,必须足够的熟悉,这是c++最重要的优势!

3:dijkstra,的复杂度是n log n的,刚开始不知道怎么没有反应过来。。。

最最基本思路:考虑每两个点之间连一条边 O(n^2),跑最短路。但是挨个建边都要n^2的复杂度。

我们发现,如果两个点之间连了一条x的边,中间还有一个点,那么|x_i - x_a|+|x_a - x_i+1| = |x_i - x_i+1|

可以只连i 到a的x的边和a到i+1的x的边,(没有直接连的,可以通过边走一定能走到,且距离和原来一样,因为是排序后建边的)

y同理只连排序后的相邻的边

用dijkstra 跑一遍最短路。稳定的时间复杂度O(nlogn) 可以过



这题的关键,还是分析题目的性质,对暴力算法进行优化,以减少一些多余的情况

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<utility>
#include<cstdlib>
#include<vector>
#include<algorithm>
#define fi first
#define se second
#define pii pair<ll,int>
#define MP(x,y) make_pair((x),(y))
using namespace std;
typedef long long ll;
const ll inf=10000000000ll;
inline int read()
{
	int ans,f=1;char ch;
	while ((ch=getchar())<'0'||ch>'9') if (ch=='-') f=-1;ans=ch-'0';
	while ((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0';
	return ans;
}

int n;
struct aa
{
	int id,x,y;
}a[200005];
bool cmpx(const aa &a,const aa &b)
{
	return a.x<b.x;
}
bool cmpy(const aa &a,const aa &b)
{
	return a.y<b.y;
}

struct bian
{
	ll dis;
	int pre,to;
}edge[200005*10];
int head[200008],tot;
void addedge(int x,int y,ll dis)
{
	edge[++tot].to=y;edge[tot].dis=dis;edge[tot].pre=head[x];head[x]=tot;
}


ll dis[200005];
bool b[200005];

int main()
{
	n=read();
	for (int i=1;i<=n;i++)
	{
		a[i].x=read();
		a[i].y=read();
		a[i].id=i;
	}
	
	sort(a+1,a+n+1,cmpx);
	for (int i=1;i<n;i++) 
	{
		int u=a[i].id,v=a[i+1].id;
		addedge(u,v,a[i+1].x-a[i].x);
		addedge(v,u,a[i+1].x-a[i].x);
	}
	sort(a+1,a+n+1,cmpy);
	for (int i=1;i<n;i++)
	{
		int u=a[i].id,v=a[i+1].id;
		addedge(u,v,a[i+1].y-a[i].y);
		addedge(v,u,a[i+1].y-a[i].y);
	}
	
	priority_queue< pii,vector< pii >,greater< pii > > heap;
	
	for (int i=1;i<=n;i++) dis[i]=inf;
	dis[1]=0;
	heap.push(MP(dis[1],1));
	
	while (!heap.empty())
	{
		pii tmp=heap.top();
		heap.pop();
		int u=tmp.se;
		
		if (b[u]) continue;
		b[u]=true;
		
		for (int i=head[u];i;i=edge[i].pre)
		if (dis[edge[i].to]>dis[u]+edge[i].dis)
		{
			dis[edge[i].to]=dis[u]+edge[i].dis;
			heap.push(MP(dis[edge[i].to],edge[i].to));
		}
	}
	
	printf("%lld",dis[n]);
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值