25.最短路问题

一、最短路

单源最短路问题:求源点 s s s 到图中其余各顶点的最短路径长度。

多源最短路问题:求图上任意两个点之间的最短路径长度。

在带权图 G = ( V , E ) G=(V,E) G=(V,E) 中,每条边都有一个权值 w i w_i wi,即边的长度。两个顶点之间的路径长度为路径上所有边权之和。

如果用我们之前学习的 d f s dfs dfs 来解决单源最短路问题,效率上会很慢,时间复杂度将是 2 2 2 的幂这一级数,能解决的问题的数据规模非常小。 而 b f s bfs bfs 能解决的最短路问题只限制在边权为 1 1 1 的图上。对于边权不同的图,利用 b f s bfs bfs 求解最短路是错误的。所以我们需要更高效的算法来帮助我们解决这两个问题。

二、Dijkstra算法

1.简介

解决单源最短路径问题常用 Dijkstra 算法,用于计算一个顶点到其他所有顶点的最短路径。Dijkstra 算法的主要特点是以起点为中心,逐步向外扩展,每次都会取一个最近点继续扩展,直到取完所有点为止。注意:Dijkstra 算法要求图中不能出现负权边

2.算法流程

我们定义带权图 G G G 所有顶点的集合为 V V V,接着我们再定义已确定从源点出发的最短路径的顶点集合为 U U U,初始集合 U U U 为空,记从源点 s s s 出发到每个顶点 v v v 的距离为 d i s t v dist_v distv,初始 d i s t s = 0 dist_s=0 dists=0 。接着执行以下操作:

  • V − U V-U VU 中找出一个距离源点最近的顶点 v v v,将 v v v 加入集合 U U U
  • d i s t v dist_v distv 和顶点 v v v 连出的边来更新和 v v v 相邻的、不在集合中的 U U U 顶点的 ,这一步称为松弛操作。
  • 重复前两个步骤,直到 V = U V=U V=U 或找不出一个从 s s s 出发有路径到达的顶点,算法结束。

如果最后 V ≠ U V\neq U V=U,说明有顶点无法从源点到达(即图不连通);否则每个 d i s t i dist_i disti 表示从 s s s 出发到顶点 i i i 的最短距离。

Dijkstra 算法的时间复杂度为 O ( V 2 ) O(V^2) O(V2),其中 V V V 表示顶点的数量。

3.模板代码

void dijkstra(ll u)
{
	memset(vis,false,sizeof(vis));
	memset(dis,0x7f,sizeof(dis));
	dis[u]=0;
	for(ll i=1;i<=n;i++)
	{
		ll mi=inf;
		for(ll j=1;j<=n;j++)
		{
			if(!vis[j] && dis[j]<mi)
			{
				mi=dis[j];
				u=j;
			}
		}
		if(mi==inf)
			return;
		vis[u]=true;
		for(ll j=p[u];j!=-1;j=e[j].next)
		{
			ll v=e[j].v,w=e[j].w;
            if(!vis[v] && dis[v]>dis[u]+w)
                dis[v]=dis[u]+w;
        }
    }
}

4.堆优化

Dijkstra 算法的核心思想就是维护一个还没有确定最短路的点的集合,每次从这个集合中选取一个路径长度最小的点确定最短路,并更新余下其他点的路径。

如果每次都暴力枚举选取距离最短的点,那么时间复杂度为 O ( V 2 ) O(V^2) O(V2)。我们完全可以考虑采用堆优化的方式,用set来维护点集,这样时间复杂度就优化到了 O ( ( V + E ) l o g   V ) O((V+E)log\ V) O((V+E)log V),对于稀疏图的优化效果非常好。

struct New
{
	ll id;
	ll len;
	bool operator<(const New &x)const
	{
		if(len!=x.len)
			return len<x.len;
		return id<x.id;
	}
};
set<New> s;
void dijkstra()
{
	memset(dist,0x7f,sizeof(dist));
	dist[S]=0;
	s.insert((New){S,0});
	while(!s.empty())
	{
		set<New>::iterator it=s.begin();
		ll d=(*it).len,u=(*it).id;
		s.erase(*it);
		vis[u]=true;
		for(ll i=p[u];i!=-1;i=e[i].next)
		{
			ll v=e[i].v;
			ll w=e[i].w;
			if(!vis[v] && dist[v]>dist[u]+w)
			{
				s.erase((New){v,dist[v]});
				dist[v]=dist[u]+w;
				s.insert((New){v,dist[v]});
			}
		}
	}
}

三、SPFA算法

1.简介

2.算法流程

3.模板代码

4.SPFA判负环

四、Floyd算法

1.简介

2.算法流程

3.模板代码

五、作业

1.dijkstra算法

P3371 【模板】单源最短路径(弱化版)

P4779 【模板】单源最短路径(标准版)

P1346 电车

你可能感兴趣的:(算法竞赛讲义,算法,图论,c++,最短路)