最短路Dijsktra+Heap堆优化(C++自带堆)

最短路历来是令各路OIer害怕的东西

本蒟蒻就分享一下我的Dijsktra+Heap优化
例题&模板题

  1. 洛谷P4779【模板】单源最短路径(标准版)
  2. 洛谷P3371【模板】单源最短路径(弱化版)


这里我的Dij是过了两题,大概是比SPFA快一点(不知道为啥SPFA在标准版下被卡飞)
进入正题

大家都知道dij每次找最小的点更新,而正常我们是线性扫一遍求最小,因此时间复杂度是
O ( n 2 ) O(n^2) O(n2) 于是我们发现线性扫一遍求最小这些字很刺眼,于是我们想到了

堆Heap!

我们只要维护一个小根堆就可以把线性扫的时间缩小到为 O ( ( m + n ) l o g n ) O((m+n)log n) O((m+n)logn)
当然,我们也可以用斐波那契堆 把时间压成 O ( m + n l o g n ) O(m+n logn) O(m+nlogn)
据wikipedia说dij+斐波那契堆是目前最快的最短路算法
然而1.本蒟蒻不会2.编程中用斐波那契堆优化常常会由于算法常数过大而导致速度没有显著提高
div--------------------------
这里本蒟蒻懒得打手动堆,于是我们就用C++中的优先队列
头文件

#include

定义

priority_queue,cmp> Q;

因为优先队列默认是“大根堆”
所以我们要手动该顺序
在定义全局变量时应该插入如下代码

struct cmp
{
    bool operator()(int a,int b)
    { 
        return dis[a]>dis[b];
    } 
};

然后简单介绍一下优先队列的基本操作
Q.push(int) 过程,将变量加入堆
int sth.=Q.top()函数,堆顶元素
Q.pop()过程,删掉堆顶
Q.empty() bool函数,堆是否为空
好了
数据结构基本操作就到这里
下面是具体堆优化操作

  1. 将源点加入堆,并调整堆
  2. 选出堆顶元素u(即代价最小的元素),从堆中删除,并对堆进行调整
  3. 处理与u相邻的,未被访问过的的顶点1)若该点在堆里,更新距离,并调整该元素在堆中的位置2)若该点不在堆里,加入堆,更新堆
  4. 若取到的u为终点,结束算法;否则重复步骤2、3

结束了,和普通dij没啥区别

存路径

和原版一样
更新dis[i]时顺便记录path[i]表示从path[i]号点转移到i号(前驱),和前向星差不多

代码

本人强迫症,用前向星存图,见谅

#include
#include
#include
#include
#include
#define INF 2147483647

using namespace std;
int n,m,cnt=0,start,end,dis[110000],vis[110000],b[110000],path[110000],print[110000];
struct map
{
	int to,val,last;
}a[550000];
struct cmp
{
    bool operator()(int a,int b)
    { 
        return dis[a]>dis[b];
    } 
};

inline int read()
{
	int x=0,f=0; char c=getchar();
	while (c<'0' || c>'9') f=(c=='-') ? 1:f,c=getchar();
	while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return f ? -x:x;
}

void add(int fr,int tow,int len)
{
	a[++cnt]=(map){tow,len,b[fr]};b[fr]=cnt;
}

void dij(int start)
{
	priority_queue,cmp> Q;
	for(int i=0;i<=n;i++)dis[i]=INF;
	memset(vis,0,sizeof(vis));
	dis[start]=0;
	Q.push(start);
	int now,got;
	while(!Q.empty())
	{
		now=Q.top();
		Q.pop();
		vis[now]=0;
		for(int i=b[now];i>0;i=a[i].last)
		{
			got=a[i].to;
			if(dis[got]==INF || dis[got]>dis[now]+a[i].val)
			{
				dis[got]=dis[now]+a[i].val;
				path[got]=now;
				if(!vis[got])
				{
					vis[got]=1;
					Q.push(got);
				}
			}
		}
	}
}

int main()
{
	n=read();m=read();start=read();end=read();
	for(int i=1;i<=m;++i)
	{
		int x,y,len;
		x=read(),y=read(),len=read();
		add(x,y,len);
	}
	dij(start);
	cnt=0;
	for(int i=path[end];i;i=path[i])print[++cnt]=i;
	for(int i=cnt;i>=1;--i)printf("%d ",print[i]);
	return 0;
}

你可能感兴趣的:(最短路Dijsktra+Heap堆优化(C++自带堆))