poj 3259 spfa/bellman判断负圈(虫洞)

本文介绍如何使用Bellman-Ford算法和SPFA算法检测有向图中的负权环。通过这两种算法可以有效地判断图中是否存在从某个源点出发可达的负权值回路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:判断有向连通图(图中有重边)中是否存在负权环。给定N个点,M条双向正权边,W条单向负权边。

思路:用bellman或者spfa均可。在bellman算法中的表述为:在求出经过了n-1条边的最短路dist[k]之后,再对每条边<u,k>判断一下:加入这条边是否会使得顶点k的最短路径值再缩短,即判断:dist[u]+w(u,k)<dist[k] 是否成立,如果成立,则说明存在从源点可达的负权值回路。

在spfa中的表述为:若一个点最短路被改进的次数达 到n ,则有负权环。

bellman版本:

#include <stdio.h>
#include <string.h>
#define N 522
int T,n,m,w,edgenum;
struct edge{
	int x,y,w;
}e[N*N/2];
int dis[N];
int bellman_ford(){
	int i,j;
	for(i = 0;i<n-1;i++){
		for(j = 0;j<edgenum;j++){
			if(dis[e[j].x]+e[j].w < dis[e[j].y]){

				dis[e[j].y] = dis[e[j].x]+e[j].w;
			}
		}
	}
	for(j = 0;j<edgenum;j++){
		if(dis[e[j].y] > dis[e[j].x]+e[j].w)
			return 1;
	}
	return 0;
}
int main(){
	//freopen("a.txt","r",stdin);
	scanf("%d",&T);
	while(T--){
		int i,x,y,len;
		edgenum = 0;
		memset(dis,0,sizeof(dis));
		scanf("%d %d %d",&n,&m,&w);
		for(i = 0;i<m;i++){
			scanf("%d %d %d",&x,&y,&len);
			e[edgenum].x = x;
			e[edgenum].y = y;
			e[edgenum++].w = len;	
			e[edgenum].x = y;
			e[edgenum].y = x;
			e[edgenum++].w = len;	
		}
		for(i = 0;i<w;i++){
			scanf("%d %d %d",&x,&y,&len);
			e[edgenum].x = x;
			e[edgenum].y = y;
			e[edgenum++].w = -len;	
		}
		if(bellman_ford())
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}

spfa版本:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define INF 0x3fffffff
#define N 1005
struct edge{
    int y,next,w;
}e[5205];
int first[N],top,dis[N],num[N],used[N];
int T,n,m,k;
queue<int> q;
void add(int x,int y,int w){
    e[top].y = y;
    e[top].w = w;
    e[top].next = first[x];
    first[x] = top++;
}
int relax(int x,int y,int w){
    if(dis[y] > dis[x]+w){
        dis[y] = dis[x]+w;
        return 1;
    }
    return 0;
}
int spfa(){
    int i,now;
    q.push(1);
    for(i = 2;i<=n;i++)
        dis[i] = INF;
    dis[1] = 0;
    memset(num, 0, sizeof(num));
    memset(used, 0, sizeof(used));
    used[1] = 1;
    while(!q.empty()){
        now = q.front();
        q.pop();
        used[now] = 0;
        for(i = first[now];i!=-1;i=e[i].next){
            if(relax(now,e[i].y,e[i].w) && !used[e[i].y]){
                used[e[i].y] = 1;
                q.push(e[i].y);
                num[e[i].y]++;
                if(num[e[i].y] == n)
                    return 1;
            }
        }
    }
    return 0;
}
int main(){
    scanf("%d",&T);
    while(T--){
        int i,a,b,w;
        while(!q.empty())
            q.pop();
        memset(first, -1, sizeof(first));
        top = 0;
        scanf("%d %d %d",&n,&m,&k);
        for(i = 1;i<=m;i++){
            scanf("%d %d %d",&a,&b,&w);
            add(a,b,w);
            add(b,a,w);
        }
        for(i = 1;i<=k;i++){
            scanf("%d %d %d",&a,&b,&w);
            add(a,b,-w);
        }
        if(spfa())
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值