算法模板:spfa

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<queue>
#include<vector>

using namespace std;
const int inf=0x7f7f7f7f;

const int NUM_NODE=310;

struct edge{ // 边的数据结构
	int to;int weight;
	edge(int t,int w):to(t),weight(w){}
};

vector<edge> e[NUM_NODE];
int dist[NUM_NODE];


bool spfa(int s){ // s是源点

	memset(dist,inf,sizeof(dist)); // 距离初始化为正无穷
	dist[s]=0; // 源点到自己距离为0
	bool inqueue[NUM_NODE]; // 用来记录顶点是否入队
	memset(inqueue,0,sizeof(inqueue));
	int qtimes[NUM_NODE]; // 用来记录入队次数
	memset(qtimes,0,sizeof(qtimes));
	queue<int> q;
	q.push(s);inqueue[s]=true;++qtimes[s]; // 将源点入队

	int cur;
	while(!q.empty()){ // while队列非空
		cur=q.front();q.pop();inqueue[cur]=false; // 取出队首
		for(edge i:e[cur]) // 对每一个队首邻接的顶点
			if(dist[i.to]>dist[cur]+i.weight){ // 如果经过队首会让你更近一些
				dist[i.to]=dist[cur]+i.weight; // 那么就更新距离
				if(!inqueue[i.to]){
					q.push(i.to);inqueue[i.to]=true;++qtimes[i.to]; // 入队
					if(qtimes[i.to]>NUM_NODE)return false; // 入队次数>顶点数,出现负环
				}
			}
	}

	return true; // 没有负环
}

int main(){
	ios::sync_with_stdio(false);

}
/*

SPFA可以处理负边,在稠密图里不如dijkstra。
复杂度是O(kE),k是每个点的平均进队次数。

1. 初始时,只有起点在队列中。
2. 遍历与起点相连的边,如果可以松弛就更新距离dist[],然后判断这个点在不在队列里,如果不在就入队标记。
3. 取出队首,取消标记,循环2-3步,直至队为空。

如何判断成环:
在储存边时,记录下每个点的入度,每个点入队的时候记录一次。
如果入队的次数大于这个点的入度,说明从某一条路进入了两次,即该点处成环。

如何判断负环:
一条最短路径最多通过所有顶点,即松弛n次,不可能从某些顶点绕好几圈。
因此,如果某个节点入队次数>n,则出现负环。

*/
posted @ 2021-10-15 17:15  MoonOut  阅读(29)  评论(0编辑  收藏  举报