Bellman_Ford和Spfa两种方法判断负环

本文探讨了一种利用Wormholes(虫洞)穿越时空的可能性,特别是如何通过一系列Wormholes回到过去。文章详细介绍了虫洞的特性,包括它们的一次性方向性、时间延迟效应、以及它们如何连接不同星系。同时,文章提供了一个程序,用于分析一组星系和虫洞数据,以确定是否存在一条循环路径,使得一个人能够无限地返回过去。通过使用Bellman-Ford和SPFA算法解决最短路径问题,该程序旨在找出是否存在这样的时间旅行路径。

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

  Bellman_Ford是通过N-1次循环求出每个点到原点的最短路的,每次循环遍历所有的边,如果能更新就更新。前面说过为什么N-1次就行(若一个点到起点的最短路需要经过N个点,包括他自己,那么第N次循环就能确定他的最短路)。

  最短路一定是不含环的,如果存在负环就根本不存在最短路了。所以这个方法可以用来判断是否有负环,如果循环了N-1次后还能进行松弛操作,说明有负环。Bellman_Ford的复杂度是O(M*N)。

 

int bellman(){   //G条边
    int i,j,k;
    memset(d,INF,sizeof(d));
    d[0]=0;
    for(i=0;i<N-1;i++){
        for(j=0;j<G;j++){
            int u=e[j].u,v=e[j].v;
                if(d[u]!=INF&&d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
        }
    }
    for(j=0;j<G;j++){
        int u=e[j].u,v=e[j].v;
            if(d[u]!=INF&&d[u]+w[u][v]<d[v]) return 1;
    }
    return 0;
}

  因为再次循环的时候有些点的d根本没有被更新,再用这个点去更新别的点的话就浪费时间了。对Bellman_Ford改进,Spfa算法是用一个队列,每次取出一个点,更新和他连接的点,如果被更新的点不在队列中就加入队列中,这样就避免了那个问题。因为是对每个点连接的点操作,这就需要链接表了(可以用vector数组)。还需要一个数组inq判断当前这个点是否在队列里。

  如果用spfa判断是否有负环问题,其实原理也是一个点不能被更新N-1次以上,所以再开一个数组记录每个点被加入队列的次数。在网上看说是一个点被加入N次以上就说明有负环,想了半天为什么是N次以上。。。后来发现因为这是有特殊情况的,N=1,这个点被加入一次,但是没负环。。。所以只要判断这个特殊情况,后面条件写成N-1次以上也是对的。。当然为了方便,直接写成N次以上更好。。网上说这个期望时间复杂度O(2*e),这个我就不知道怎么算的了。。。

int spfa(){
    queue<int> q;
    memset(d,INF,sizeof(d));
    memset(inq,0,sizeof(inq));
    memset(cnt,0,sizeof(cnt));
    d[0]=0;
    inq[0]=1;
    q.push(0);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        inq[x]=0;
        cnt[x]++;
        if(cnt[x]>N) return 1;
        if(d[x]==INF) continue;
        for(int i=0;i<v[x].size();i++){
            int y=v[x][i].y;
            if(d[x]+v[x][i].t<d[y]){
                d[y]=d[x]+v[x][i].t;
                inq[y]=1;
                q.push(y);
            }
        }
    }
    return 0;
}

  Wormholes 

In the year 2163, wormholes were discovered. A wormhole is a subspace tunnel through space and time connecting two star systems. Wormholes have a few peculiar properties:

  • Wormholes are one-way only.
  • The time it takes to travel through a wormhole is negligible.
  • A wormhole has two end points, each situated in a star system.
  • A star system may have more than one wormhole end point within its boundaries.
  • For some unknown reason, starting from our solar system, it is always possible to end up in any star system by following a sequence of wormholes (maybe Earth is the centre of the universe).
  • Between any pair of star systems, there is at most one wormhole in either direction.
  • There are no wormholes with both end points in the same star system.

All wormholes have a constant time difference between their end points. For example, a specific wormhole may cause the person travelling through it to end up 15 years in the future. Another wormhole may cause the person to end up 42 years in the past.


A brilliant physicist, living on earth, wants to use wormholes to study the Big Bang. Since warp drive has not been invented yet, it is not possible for her to travel from one star system to another one directly. Thiscan be done using wormholes, of course.


The scientist wants to reach a cycle of wormholes somewhere in the universe that causes her to end up in the past. By travelling along this cycle a lot of times, the scientist is able to go back as far in time as necessary to reach the beginning of the universe and see the Big Bang with her own eyes. Write a program to find out whether such a cycle exists.

Input 

The input file starts with a line containing the number of cases c to be analysed. Each case starts with a line with two numbersn andm . These indicate the number of star systems ($1 \le n \le 1000$) and the number of wormholes ($0 \le m \le 2000$) . The star systems are numbered from 0 (our solar system) throughn-1 . For each wormhole a line containing three integer numbersx,y andt is given. These numbers indicate that this wormhole allows someone to travel from the star system numberedx to the star system numberedy, thereby ending upt ($-1000 \le t \le 1000$) years in the future.

Output 

The output consists of c lines, one line for each case, containing the wordpossible if it is indeed possible to go back in time indefinitely, ornot possible if this is not possible with the given set of star systems and wormholes.

Sample Input 

2
3 3
0 1 1000
1 2 15
2 1 -42
4 4
0 1 10
1 2 20
2 3 30
3 0 -60

Sample Output 

possible
not possible

#include<cstring>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#define INF 0x3f3f3f3f
#define MAXN 1010
#define pii pair<int,int>
using namespace std;
int N,M,G,w[MAXN][MAXN],d[MAXN],inq[MAXN],cnt[MAXN];
struct edge{
    int y,t;
    edge(int a,int b){
        y=a;
        t=b;
    }
};
vector<edge> v[MAXN];

struct Bellman_edge{
    int u,v,t;
    Bellman_edge(){}
    Bellman_edge(int a,int b,int d){
        u=a;
        v=b;
        t=d;
    }
}e[2*MAXN];

int bellman(){   //G条边
    int i,j,k;
    memset(d,INF,sizeof(d));
    d[0]=0;
    for(i=0;i<N-1;i++){
        for(j=0;j<G;j++){
            int u=e[j].u,v=e[j].v;
                if(d[u]!=INF&&d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
        }
    }
    for(j=0;j<G;j++){
        int u=e[j].u,v=e[j].v;
            if(d[u]!=INF&&d[u]+w[u][v]<d[v]) return 1;
    }
    return 0;
}
int spfa(){
    queue<int> q;
    memset(d,INF,sizeof(d));
    memset(inq,0,sizeof(inq));
    memset(cnt,0,sizeof(cnt));
    d[0]=0;
    inq[0]=1;
    q.push(0);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        inq[x]=0;
        cnt[x]++;
        if(cnt[x]>N) return 1;
        if(d[x]==INF) continue;
        for(int i=0;i<v[x].size();i++){
            int y=v[x][i].y;
            if(d[x]+v[x][i].t<d[y]){
                d[y]=d[x]+v[x][i].t;
                inq[y]=1;
                q.push(y);
            }
        }
    }
    return 0;
}
int main(){
    freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&N,&M);
        memset(w,INF,sizeof(w));
        int i,a,b,t;
        G=0;
        for(i=0;i<N;i++) v[i].clear();
        for(i=0;i<N;i++) w[i][i]=0;
        for(i=0;i<M;i++){
            scanf("%d%d%d",&a,&b,&t);
            w[a][b]=t;                      //bellman
            e[G++]=Bellman_edge(a,b,t);
            v[a].push_back(edge(b,t));      //spfa
        }
        if(bellman()) printf("possible\n");
        else printf("not possible\n");
    }
    return 0;
}



<think>好的,我需要回答用户关于Bellman-Ford算法SPFA的实现与比较的问题。首先,我得回顾一下这两个算法的基本概念原理。Bellman-Ford算法是用来解决单源最短路径问题的,可以处理带有权边的图,而SPFA(队列优化的Bellman-Ford)是对其的一种优化,通过减少不必要的松弛操作来提高效率。 接下来,我需要确保按照用户提供的系统级指令来组织内容。首先,所有的行内数学公式要用$...$,比如时间复杂度O(VE)应该写成$O(VE)$。独立公式需要用$$...$$并且单独成段,比如松弛操作的公式可能需要单独列出。 然后,需要检查LaTeX语法是否正确,比如积分符号\int上下标是否正确。同时,使用中文回答,并且生成相关问题。用户还提到在引用的段落末尾添加引用标识,这里参考的引用[1]是关于SPFA的优化,需要在适当的地方插入[^1]。 用户的问题包括实现比较,所以需要分别给出Bellman-FordSPFA的代码示例。Bellman-Ford的Python实现通常包括遍历所有边进行松弛操作,而SPFA则使用队列来维护需要松弛的节点,减少重复计算。在代码部分要注意格式,使用三个反引号包裹代码块,并标明语言为python。 在比较部分,需要列出时间复杂度、空间复杂度、适用场景以及各自的优缺点。比如Bellman-Ford的时间复杂度是$O(VE)$,而SPFA的平均情况下是$O(E)$,最坏情况下还是$O(VE)$。同时,SPFA在稀疏图上表现更好,但在存在的情况下需要检测机制。 最后生成的相关问题要围绕算法差异、SPFA的优化、检测以及应用场景展开。确保每个问题都有针对性,能够帮助用户进一步理解这两个算法。 现在需要整合所有内容,确保结构清晰,分步骤解释Bellman-FordSPFA的实现,然后进行比较。同时,正确引用提供的参考内容,特别是在讨论SPFA优化时,添加[^1]的引用标识。还要检查数学公式的格式是否正确,比如松弛操作的公式是否使用行内或独立格式,以及代码示例是否符合要求。 可能需要注意的细节包括:Bellman-Ford是否能检测SPFA如何检测,以及在实际代码中如何处理这些情况。例如,Bellman-Ford通过第V次松弛是否还能更新来判断,而SPFA通过记录节点的入队次数是否超过V-1次。 最后,确保生成的相关问题与内容相关,并且能够引导用户深入探索,比如询问优化方法、性能比较、检测方法等。</think>### Bellman-Ford算法与SPFA对比分析 #### 1. Bellman-Ford算法实现 **核心思想**:通过$V-1$轮对所有边的松弛操作,逐步逼近最短路径值。能检测(若第$V$轮仍能松弛则存在)。 **时间复杂度**:$O(VE)$ **适用场景**:含权边的图、需检测的场景 ```python def bellman_ford(graph, start): n = len(graph) distance = [float('inf')] * n distance[start] = 0 for _ in range(n-1): for u, v, w in graph.edges: if distance[u] + w < distance[v]: distance[v] = distance[u] + w # 检测 for u, v, w in graph.edges: if distance[u] + w < distance[v]: return "存在" return distance ``` #### 2. SPFA实现(队列优化) **优化原理**:使用队列存储需要松弛的节点,避免无效松弛。只有当某个节点的最短距离更新时,才将其相邻节点加入队列。 **时间复杂度**:平均$O(E)$,最坏仍为$O(VE)$ **适用场景**:稀疏图、无的图 ```python def spfa(graph, start): n = len(graph) distance = [float('inf')] * n in_queue = [False] * n counter = [0] * n # 检测 queue = deque() distance[start] = 0 queue.append(start) in_queue[start] = True while queue: u = queue.popleft() in_queue[u] = False for v, w in graph.adj[u]: if distance[u] + w < distance[v]: distance[v] = distance[u] + w if not in_queue[v]: queue.append(v) in_queue[v] = True counter[v] += 1 if counter[v] > n-1: return "存在" return distance ``` #### 3. 关键对比 | 特性 | Bellman-Ford | SPFA | |---------------------|-----------------------|----------------------| | **时间复杂度** | $O(VE)$ | 平均$O(E)$,最坏$O(VE)$ | | **空间复杂度** | $O(V)$ | $O(V+E)$ | | **权处理** | 支持 | 支持但需额外检测 | | **实现复杂度** | 简单 | 中等 | | **适用场景** | 稠密图/强检测需求 | 稀疏图/高效场景 | #### 4. 数学原理 松弛操作公式: $$d[v] = \min(d[v], d[u] + w(u,v))$$ SPFA的优化体现在通过队列维护更新节点集合,减少冗余计算。该优化基于动态逼近理论,只有当$d[u]$发生变化时,$u$的后继节点才可能需要进行松弛。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值