最短路问题总结

南昌理工学院acm集训队

从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径叫做最短路径。解决最短路的问题有以下算法,Dijkstra算法Bellman-Ford算法Floyd算法SPFA算法等。

目录

最短路算法框架

朴素Dijkstra算法

堆优化版Dijkstra算法

Bellman-Ford算法

 SPFA

SPFA判断负环

Floyd算法


最短路算法框架

最短路有五种算法,分别适用不同的情况。

单源最短路: 求一个点到其他点的最短路
多源最短路: 求任意两个点的最短路

稠密图用邻接矩阵存,稀疏图用邻接表存储。

朴素Dijkstra算法

在这里插入图片描述

堆优化版Dijkstra算法

优化版的Dijkstra算法是通过小根堆来找到当前堆中距离起点最短且没有确定最短路的那个点。

因为是稀疏图,所以需要用邻接表来存储。

Bellman-Ford算法

Bellman-Ford算法是通过循环 n 次,每次循环都遍历每条边,进而更新结点的距离,每一次的循环至少可以确定一个点的最短路,所以循环 n次,就可以求出 n 个点的最短路。

在这里插入图片描述

 SPFA

SPFA算法需要图中没有负环才能使用。其实大部分正权图也是可以用SPFA算法做的,例如最上面的那到题就可以用SPFA做,效率还高于Dijkstra算法。

在这里插入图片描述


int spfa()
{// bool st[N]: 存第 i 个点是不是在队列中,防止存重复的点 
	memset(dist,0x3f,sizeof(dist));
	dist[1] = 0;
	
	queue<int> q; //存储所有待更新的点
	q.push(1);  // 1号点入队 
	st[1] = true;
	while(q.size()) // 队列不空
	{
	   int t = q.front(); //取队头 
	   q.pop();
	   st[t] = false; // 代表这个点已经不在队列了,因为存在边权为负数,某个点可能会被更新多次,所以可以多次入队和出队。
	   
	   for(int i = h[t]; i!=-1; i=ne[i]) // 更新 t 的所有临边结点的最短路 
	   {
	   	 int j = e[i];
	   	 if(dist[j] > dist[t]+w[i])
	   	 {
	   	    dist[j] = dist[t] + w[i];
			if(!st[j])  //如果 j 不在队列,让 j 入队 
			{
				q.push(j); 
				st[j] = true;  // 标记 j 在队中 
			} 	    	
		 }
	   }	
	} 
	 if(dist[n] == 0x3f3f3f3f) return -1; // 不存在最短路 
	 return dist[n]; 
}

SPFA判断负环

什么是负环呢? 下图左边的2——>3——>4就是一个负环,因为转一圈后的距离是负的,右图的 1 结点是应该自环,也属于负环。
在这里插入图片描述

int spfa()
{
	queue<int> q; 
    for(int i=1; i<=n; i++) //将所有结点入队
	{
	    st[i] = true;
		q.push(i);	  
    }
	while(q.size()) // 队列不空
	{
	   int t = q.front(); //取队头 
	   q.pop();
	   st[t] = false; // 代表这个点已经不在队列了
	   
	   for(int i = h[t]; i!=-1; i=ne[i]) // 更新 t 的所有临边结点的最短路 
	   {
	   	 int j = e[i];
	   	 if(dist[j] > dist[t]+w[i])
	   	 {
	   	    dist[j] = dist[t] + w[i];
	   	    cnt[j] = cnt[t] + 1; // t到起点的边数+1 
	   	    
	   	    if(cnt[j] >= n) return true;// 存在负环 
			if(!st[j])  //如果 j 不在队列,让 j 入队 
			{
				q.push(j); 
				st[j] = true;  // 标记 j 在队中 
			} 	    	
		 }
	   }	
	} 
	 return false;// 不存在负环 
}

Floyd算法

Floyd算法是基于动态规划的,从结点 i 到结点 j 的最短路径只有两种:
1、直接 i 到 j
2、i 经过若干个结点到 k 再到 j
对于每一个k,我们都判断 d[i][j] 是否大于 d[i][k] + d[k][j],如果大于,就可以更新d[i][j]了。

void floyd()
{
 for(int k=1; k<=n; k++)
   for(int i=1; i<=n; i++)
     for(int j=1; j<=n; j++)
      d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

三次循环完之后,遍历完所有的 k 后,d[i][j] 存的就是 i——>j的最短路了。
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值