SPFA

簡單介紹一下\(SPFA\):先把全部點的值賦爲INF,而後找到起點,標位零,將其壓入隊列(都到這裏了,應該沒人不會隊列了吧…),下面的步驟要循環經行,直到隊列爲空。找到延生出來的節點,若是節點的值大於當前的點的值加邊的長度(設當前節點的值是\(v_i\),延伸出來的點的值是\(v_j\),邊的長度是\(d_i\),則上面那句話的意思是若是\(v_i\)+\(d_i\)>\(v_j\)),就更新並加入隊列。
具體實現就是這個亞子:

看到第三幅圖的那個箭頭了嗎?那裏是不能加入隊列的,由於隊列裏已經有4號點了,這是\(SPFA\)最容易打錯的地方。

(圖片來源於洛谷2019夏令營的課件)
這裏再說一下有關\(SPFA\)的其餘的東西:c++

  1. 上面更新點的最短路的操做稱爲鬆弛操做
  2. \(SPFA\)能夠有負權邊,但\(dij\)不行
  3. \(SPFA\)也能夠判負權環,就是一個點被鬆弛了n次,該題沒有負權環,由於負權環會使最短路無限小,而題面沒有給出處理方法

接下來是最激動人心的時刻:上代碼!!!spa

#include<bits/stdc++.h>
using namespace std;
int n,m,s;
queue<int>q;
bool f[10005];//用於標記是否加入隊列
int v[10005];//每一個點離起點的最短路徑
struct graph
{
	int tot;
	int hd[10005];
	int nxt[500005],to[500005],dt[500005];
	void add(int u,int v,int w)
	{
		tot++;
		nxt[tot]=hd[u];
		hd[u]=tot;
		to[tot]=v;
		dt[tot]=w;
		return ;
	}
}g;
int main()
{
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		g.add(u,v,w);
	}
	memset(v,0x3f,sizeof(v));//初始化,初始化必須是一個很大的值,不然若是實際路徑比初始化長就會出問題
	v[s]=0;
	f[s]=true;
	q.push(s);
	while(!q.empty())//直到隊列爲空爲止
	{
		int xx=q.front();
		q.pop();//取出第一個元素並彈出
		f[xx]=false;//標記爲沒有加入隊列
		for(int i=g.hd[xx];i;i=g.nxt[i])//枚舉該節點延伸出來的點
			if(v[g.to[i]]>v[xx]+g.dt[i])//鬆弛操做
			{
	 			if(!f[g.to[i]]) q.push(g.to[i]),f[g.to[i]]=true;//加入隊列
				v[g.to[i]]=v[xx]+g.dt[i];//更新
			}
	}
	for(int i=1;i<=n;i++)
		if(v[i]!=1e10) printf("%d ",v[i]);
		else printf("2147483647 ");
	return 0;
}
相關文章
相關標籤/搜索