spfa优化时间复杂度及判负环

NOI2018 Day1 T1有许多人用spfa被卡掉25分。

注意:写spfa绝对不用STL,因为出题人会不怀好意的卡掉它。

我也是愿意写spfa的人.(毕竟简单)。在这里总结一下spfa的优化

1.普通spfa会开一个数组。可以用循环队列优化.(教程书中都有).

2.我们在把点加入队列时,随机从队首或队尾加入(几乎没人会卡掉)。

如果嫌麻烦,可以开成栈。

3.设要加入的节点是j,队首元素为i,若dist(j) < dist(i),则将j插入队首,否则插入队尾。

4.设队首元素为i,每次弹出时进行判断,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。

这几种方法选一种或两种就够了。

普通spfa就不说了。

负环就是在普通spfa上加一句话,若扩展的点已经被扩展n次就说明有负环了。

题目:VIJOS P1053 Easy sssp

代码如下:

#include<cstdio>
#include<cstring>
#define N 101000
int n,m,s;
int to[N];
int head[N];
int nex[N];
int val[N];
int f[N];
int que[N];
int cnt[N];
int is[N];
int inq[N];
int a,b,c,idx;
void push(int &x)
{
	x++;
	if(x==N)
		x=1;
}
void addedge(int a,int b,int c)
{
	nex[++idx]=head[a];
	head[a]=idx;
	to[idx]=b;
	val[idx]=c;
}
bool spfa(int s)
{
	for(int i=1;i<=n;i++)
		inq[i]=0,cnt[i]=0,f[i]=0x3f3f3f3f;
	f[s]=0;
	cnt[s]=1;
	inq[s]=1;
	int front=0,tail=0;
	que[tail]=s;
	push(tail);
	while(front!=tail)
	{
		int idx1=que[front];
		push(front);
		inq[idx1]=0;
		for(int i=head[idx1];i;i=nex[i])
		{
			if(f[to[i]]>f[idx1]+val[i])
			{
				f[to[i]]=f[idx1]+val[i];
				if(!inq[to[i]])
				{
					inq[to[i]]=1;
					que[tail]=to[i];
					push(tail);
					cnt[to[i]]++;//记录入队次数
					if(cnt[to[i]]>n)
						return 0;					
				}
			}
		}
	}
	return 1;
}
int main()
{
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		if(a==b&&c<0)
		{
			puts("-1");
			return 0;
		}
		if(a!=b)
			addedge(a,b,c);
	}
	memset(f,0x3f,sizeof(f));
	memset(que,0,sizeof(que));
	int maxn=n+1;//建一个空节点,用这个点跑spfa
	for(int i=1;i<=n;i++)
		addedge(maxn,i,0);
	if(!spfa(maxn))
	{
		puts("-1");
		return 0;
	}
	spfa(s);
	for(int i=1;i<=n;i++)
	{
		if(f[i]>=0x3f3f3f3f)
			printf("NoPath\n");
		else
			printf("%d\n",f[i]);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/342zhuyongqi/p/9801331.html