spfa算法判断负环

spfa求负环:

分析:为什么可以通过spfa求负环? 

dist[x]表示x点距离源点的距离,cnt[x]表示从源点到x点经过的边数

有:

if(dist[x] > dist[t] + w[i])
{
     dist[x] = dist[t] + w[i];
     cnt[x] = cnt[t] + 1;
} 

cnt[x] = cnt[t] + 1 的意思是 如果距离更新了,那么从源点到x的边数就等于源点到t的边数+ t到x的边数(即一条边)

所以通过这个我们可以判断是否存在负环,如果在x,t之间存在负环,那么cnt[x] 会不断加1,我们通过判断如果cnt[x] >= n  进而确定是否存在负环。

为什么是cnt[x] >= n ? 因为cnt数组表示的是边数,如果从源点到x点的边数大于等于n,那么在源点和x点之间肯定存在n+1个点,但是最多只有n个点,所以

必然有点重复出现,从而出现负环 !

同时我们并不知道出现负环的地方是否与源点连通,所以我们要将所有的点都提前放进队列中,这样就可以判断所有的点。

代码:

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 100010;
int e[N], ne[N], h[N], w[N], dist[N], idx, cnt[N];
bool st[N];
int n, m;

void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

bool spfa()
{
    queue<int> q;
    memset(dist, 0x3f, sizeof dist);
    for(int i = 1; i <= n; i++)
    {
        st[i] = true;
        q.push(i);
    }
    
    while(q.size())
    {
        int t = q.front(); q.pop();
        st[t] = false;
        
        for(int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if(cnt[j] >= n) return true;
                if(!st[j])
                {
                    st[j] = true;
                    q.push(j);
                }
            }
        }
    }
    return false;
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    while(m--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    
    if(spfa()) puts("Yes");
    else puts("No");
    
    return 0;
}

 

posted @ 2022-02-23 09:32  飘向远方丶  阅读(119)  评论(0编辑  收藏  举报