SPFA算法

SPFA算法是一种优化版的Bellman-Ford算法,通过队列优化减少冗余计算,平均时间复杂度为O(kE)。算法核心是节点可能多次进出队列,用于寻找最短路径。文中还提及了Dijkstra算法的训练题,用于求解最短路径问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SPFA算法

SPFA是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。
主要思想是:
初始时将起点加入队列。每次从队列中取出一个元素,并对所有与它相邻的点进行修改,若某个相邻的点修改成功,则将其入队。直到队列为空时算法结束。
这个算法,简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点发明的此算法。
SPFA 在形式上和广度优先搜索非常类似,不同的是广度优先搜索中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也就是说一个点修改过其它的点之后,过了一段时间可能会获得更短的路径,于是再次用来修改其它的点,这样反复进行下去。
算法时间复杂度:O(kE),E是边数。K是常数,平均值为2。
算法实现:
dis[i]记录从起点s到i的最短路径,w[i][j]记录连接i,j的边的长度。pre[v]记录前趋。
team[1…n]为队列,头指针head,尾指针tail。
布尔数组exist[1…n]记录一个点是否现在存在在队列中。
初始化:dis[s]=0,dis[v]=∞(v≠s),memset(exist,false,sizeof(exist));
起点入队team[1]=s; head=0; tail=1;exist[s]=true;
do
{
1、头指针向下移一位,取出指向的点u。
2、exist[u]=false;已被取出了队列
3、for与u相连的所有点v //注意不要去枚举所有点,用数组模拟邻接表存储
if (dis[v]>dis[u]+w[u][v])
{
dis[v]=dis[u]+w[u][v];
pre[v]=u;
if (!exist[v]) //队列中不存在v点,v入队。
{
//尾指针下移一位,v入队;
exist[v]=true;
}

}
while (head < tail);

【训练题】Dijkstra算法
Description

重庆城里有n个车站,m条双向公路连接其中的某些站。每两个车站最多用一条公路直接相连,从任何一个车站出发都可以经过一条或多条公路到达其它车站,但不同的路径需要花费的时间可能不同。在一条路径上花费的时间等于路径上所有公路需要的时间和。

HB家住第s个车站,打算用漫长暑假走完重庆城的每个角落,现在需要知道从他家出发,到每个车站的最短时间。

Input
  第一行:N(N<=50,000),M(M<=100,000),为车站数目和公路的数目。  接下来m行,每行三个整数x,y,t( 1<=x,y<=N,1<=t<=100),为公路连接的两个车站编号和时间。  最后一行一个整数s。

Output
  共N行,分别表示从s到车站1、2、…、N的最短时间。
Sample Input 1
6 10
1 2 4
1 3 8
2 3 3
2 4 4
2 5 6
3 4 2
3 5 2
4 5 4
4 6 9
5 6 4
1

Sample Output 1
0
4
7
8
9
13

Hint
N<=50000

#include<bits/stdc++.h>
using namespace std;
int n,m;
int num[50005];
int dis[50005];  //从原点到i点的总时间
int v[50005];  //0表示不再队列中,1表示在队列中
queue<int> q;

struct student{
	int to;
	int val;
}g[50005][100];

int main(){
	scanf("%d%d",&n,&m);
	memset(g,0x7f/3,sizeof(g));
	for(int i=1;i<=m;i++){
		int x,y,t;
		scanf("%d%d%d",&x,&y,&t);
		g[x][++num[x]].to=y;
		g[y][++num[y]].to=x;
		g[x][num[x]].val=t;
		g[y][num[y]].val=t;  //邻接表存储
	}
	int s;
	scanf("%d",&s);
	memset(dis,0x7f/3,sizeof(dis));
	dis[s]=0;
	q.push(s);
	v[s]=1;
	do{
		int i=q.front();     //队列实现
		q.pop();
		v[i]=0;
		for(int j=1;j<=num[i];j++){
			if(dis[g[i][j].to]>dis[i]+g[i][j].val){  //SPFA算法
				dis[g[i][j].to]=dis[i]+g[i][j].val;
				if(!v[g[i][j].to]){
					v[g[i][j].to]=1;
					q.push(g[i][j].to);
				}
			}
		}
	}while(!q.empty());
	for(int i=1;i<=n;i++){
		printf("%d\n",dis[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值