POJ2679 SPFA求负环

本文介绍了一种使用SPFA算法寻找图中是否存在负环的方法,并详细解释了如何通过限制顶点出队次数来检测负环的存在。同时,文章还提供了一个完整的C++实现代码示例,包括特殊的边删除技巧及确保算法正确性的注意事项。
SPFA求负环
注意事项:
	1. 不要让一个点出队n次就算负环,如果只有一个点呢?出队n+1次费不了多少时间。
	2. 从终点往回遍历不到的点即便有负环也无所谓,因为它根本无法在s → t的路径上累加负权值。
	3. 此题注意删边方法。
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define N 3000
#define M 101000
using namespace std;
//只能走权值最小的边 
struct Syndra
{
	int u,v,w,len,next;
}e[M];
int head[N],cnt;
int dist[N],len[N],visit[N],s,t,n,m;
int mini[N],able[N],num[N];
void add(int u,int v,int w,int len)
{
	cnt++;
	e[cnt].u=u;
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].len=len;
	e[cnt].next=head[u];
	head[u]=cnt;
}
void spfa()
{
	int i,j,k,u,v;
	queue<int> q;
	memset(dist,0x3f,sizeof(dist));
	memset(visit,0,sizeof(visit));
	memset(len,0x3f,sizeof(len));
	memset(num,0,sizeof(num));
	q.push(s);
	dist[s]=len[s]=0;
	visit[s]=1;
	while(!q.empty())
	{
		u=q.front();
		q.pop();
		visit[u]=0;
		if(!able[u])continue;
		num[u]++;
		if(num[u]>n)
		{
			printf("UNBOUND\n");
			return;
		}
		for(i=head[u];i+1;i=e[i].next)
		{
			v=e[i].v;
			if(!able[v])continue;
			if(e[i].w>mini[u])continue;
			if(dist[v]>dist[u]+e[i].w)
			{
				dist[v]=dist[u]+e[i].w;
				len[v]=len[u]+e[i].len;
				if(!visit[v])
				{
					visit[v]=1;
					q.push(v);
				}
			}
			else if(dist[v]==dist[u]+e[i].w)
			{
				if(len[v]>len[u]+e[i].len)
				{
					len[v]=len[u]+e[i].len;
					if(!visit[v])
					{
						visit[v]=1;
						q.push(v);
					}
				}
			}
		}
	}
	if(dist[t]==0x3f3f3f3f)
	{
		printf("VOID\n");
		return ;
	}
	else
	{
		printf("%d %d\n",dist[t],len[t]);
		return ;
	}
}
char a,b,c;
void dfs(int x)
{
	int i;
	if(able[x])return ;
	able[x]=1;
	for(i=head[x];i+1;i=e[i].next)
	{
		if(e[i^1].w==mini[e[i].v])dfs(e[i].v);
	}
}
void build()
{
	int i,j,k;
	cnt=-1;
	memset(num,0,sizeof(num));
	memset(able,0,sizeof(able));
	memset(head,-1,sizeof(head));
	memset(mini,0x3f,sizeof(mini));
	for(i=1;i<=m;i++)
	{
		int u,v,w1,l,w2;
		scanf(" %c%d%c%d%c%d%c%d%c%d%c",&a,&u,&a,&v,&a,&w1,&a,&l,&a,&w2,&a);
		add(u,v,w1,l);
		add(v,u,w2,l);
		mini[u]=min(mini[u],w1);
		mini[v]=min(mini[v],w2);
	}
	dfs(t);
}
int main()
{
//	freopen("test.in","r",stdin);
	while(scanf("%d%d%d%d",&n,&m,&s,&t)!=EOF)
	{
		build();
		spfa();	
	}
	return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值