Bellman-Ford算法、SPFA算法模板——含负边权单源最短路问题 及 负环判断问题

Bellman-Ford算法:

核心思路: 源点d值设为0,其他d值设为INF。执行N-1次操作(N为结点数),每次遍历所有边来进行松弛操作。N-1次操作结束后,再遍历一次所有边,若还能继续松弛,说明有源点可达的负环。

模板:

struct edge
{
	int u;
	int v;
	int w;
} a[maxm];
int N,M;       //N个结点,M条边
int d[maxn];
bool BF(int s)
{
	fill(d,d+maxn,INF);
	d[s]=0;
	for(int i=1;i<=N-1;i++)        //进行N-1次操作
	{
		for(int j=1;j<=M;j++)      //每轮操作遍历所有边
		{
			int u=a[j].u,v=a[j].v,w=a[j].w;
			if(d[u]+w<d[v])        //以u为中介点可以使d[v]更小
				d[v]=d[u]+w;       //松弛操作
		}
	}
	for(int j=1;j<=M;j++)          //再遍历一遍
	{
		int u=a[j].u,v=a[j].v,w=a[j].w;
		if(d[u]+w<d[v])            //如果仍然可以被松弛
			return false;          //说明有源点可达的负环
	}
	return true;
}

PS. 这里可以进行一点优化,若在N-1轮操作内的某一轮没有进行松弛操作,那么说明已求得最短路,可以直接结束。



SPFA算法:

核心思路: 由于只有当某个结点u的d[u]值被改变时,从它出发的边的邻接点v的d[v]值才有可能被改变,那么就可以利用队列对BF算法进行优化,得到SPFA算法。另外当 某个结点的入队次数 > N-1 时,说明有源点可达的负环。

模板:

int G[maxn][maxn]
int N;
int d[maxn],cnt[maxn];     //cnt记录结点入队次数
bool inq[maxn];            //inq标记结点是否在队列中
bool SPFA(int s)
{
	fill(d,d+maxn,INF);
	memset(cnt,0,sizeof(cnt)); 
	memset(inq,false,sizeof(inq));
	queue<int> q;
	d[s]=0;
	q.push(s);              //源点入队
	inq[s]=true;
	cnt[s]++;               //源点入队次数+1
	if(cnt[s]>N-1)
		return true;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();            //出队
		inq[u]=false;
		for(int v=1;v<=N;v++)
		{
			if(G[u][v]!=INF&&d[u]+G[u][v]<d[v])
			{
				d[v]=d[u]+G[u][v];        //进行松弛操作
				if(!inq[v])               //若v不在队列中,则入列
				{                         //注意该判断语句不能放到上一个if中!!!
					q.push(v);
					inq[v]=true;
					cnt[v]++;             //入队次数+1
					if(cnt[v]>N-1)        //若入队次数>N-1时,说明有源点可达的负环
						return true;		
				}
			}
		}
	}
	return false;
}

最后还要注意一点,BF算法和SPFA算法只能判断是否存在源点可达的负环,对于源点不可达的(即和源点不属于同一连通块),无法判断负环存在与否。

你可能感兴趣的:(★Tips,★图论,#,【最短路】)