SPFA算法,队列优化的Bellman-Ford算法详解:判断负环P3385

P3385 【模板】负环 - 洛谷

本文参考了如下题解,记录了自己的理解

题解 P3385 【【模板】负环】 - 洛谷专栏

数据结构

  • dis[v]:1到v的最短距离(初值正无穷,不断更新)
  • cnt[v]:1到v的最短路径的边数(到n退出,原因见下)
  • vis[v]:标记v当前是否在队列中(避免有多条路径到v时,v重复入队

原理

在 没有负环 的图中,任意两个节点之间的最短路径最多包含 n-1 条边。因为 n 个节点的路径最多有 n-1 条边,否则就会形成环,又由“最短路径”的加入条件可知此环为负环。比如有三个点,由1走到2走到3,此时已走过2条边,如果又能加入1(满足dis[3] + d(1,3) < dis[1]),则1与1的最短路径就包含了3条边,一定存在负环。

代码 

核心:不断出队,遍历邻接点,判断最短路径,考虑是否加入新点 

#include <bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
const int N = 2e3 + 5;
const ll INF = LLONG_MAX;
int t, n, m;
int cnt[N];
ll dis[N];
bool vis[N];

struct edge {
    int v, w;
    edge(int x, int y) {
        v = x, w = y;
    }
};

inline void judge(vector<edge> e[]) {
    queue<int> q;
    q.push(1);
    dis[1] = 0, vis[1] = 1, cnt[1] = 0;

    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (auto& it : e[u]) {
            int v = it.v, w = it.w;
            if (dis[u] + w < dis[v]) {
                dis[v] = dis[u] + w;
                cnt[v] = cnt[u] + 1;
                if (cnt[v] >= n) {
                    cout << "YES" << endl;
                    return;
                }
                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    cout << "NO" << endl;
}

int main()
{
    IOS;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        vector<edge> e[N];
        memset(vis, 0, sizeof vis);
        memset(cnt, 0, sizeof cnt);
        fill(dis + 1, dis + 1 + n, INF);
        for (int i = 1; i <= m; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            if (w >= 0)
            {
                e[u].push_back(edge(v, w));
                e[v].push_back(edge(u, w));
            }
            else {
                e[u].push_back(edge(v, w));
            }
        }
        judge(e);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值