ACM - 图论 - P3385 负环

P3385 负环

题目描述

给定一个 \(n\) 个点的有向图,请求出图中是否存在从顶点 \(1\) 出发能到达的负环。

负环的定义是:一条边权之和为负数的回路。

输入格式

本题单测试点有多组测试数据。

输入的第一行是一个整数 \(T\),表示测试数据的组数。对于每组数据的格式如下:

第一行有两个整数,分别表示图的点数 \(n\) 和接下来给出边信息的条数 \(m\)

接下来 \(m\) 行,每行三个整数 \(u\)\(v\)\(w\)

\(w \geqslant 0\),则表示存在一条从 \(u\)\(v\) 边权为 \(w\) 的边,还存在一条从 \(v\)\(u\) 边权为 \(w\) 的边。
\(w < 0\),则只表示存在一条从 \(u\)\(v\) 边权为 \(w\) 的边。

输出格式

对于每组数据,输出一行一个字符串,若所求负环存在,则输出YES,否则输出NO

输入输出样例

输入 #1

2
3 4
1 2 2
1 3 4
2 3 1
3 1 -3
3 3
1 2 3
2 3 4
3 1 -8

输出 #1

NO
YES

数据规模

对于全部的测试点,保证:

  • \( 1 \leqslant n \leqslant 2 \times 10^{3}, 1 \leqslant m \leqslant 3 \times 10^{3} \)

  • \( 1 \leqslant u,v \leqslant n, 10^{-4} \leqslant w \leqslant 10^{-4} \)

  • \( 1 \leqslant T \leqslant 10 \)

题解

该题是一个判断给定图中是否存在负环(或负权回路)的模板题。

使用 \(Bellman-Ford\) 算法判负环。补充,\(SPFA\) 算法也可以判负环,但个人感觉没有该算法自然,而且两者时间复杂度相差不大,感兴趣的都可以实现一下,之后有时间会在此题补充图论中判负环的算法。

代码实现如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#define PII pair<int, int> 
#define INF 0x3f3f3f3f
using namespace std;

struct edge
{
    int u;  // 边的起点
    int v;  // 边的终点
    int w;  // 边的权值
};

const int N = 2005;  // 最大点数
const int M = 3005;  // 最大边数
int n, m, q;
vector<edge> es;   // 用邻接表存储边
int dist[N];       // 第i号点距离源点的当前最短距离
int vis[N];        // vis数组存的是当前结点是否在队列中

int releax(int e)
{
    if (dist[es[e].u] == INF) {
        return 0;
    }
    else if (dist[es[e].u] + es[e].w < dist[es[e].v]) {
        dist[es[e].v] = dist[es[e].u] + es[e].w;
        return 1;
    }
    return 0;
}

bool bellman()
{
    int sz = es.size();
    for (int i = 1; i <= n; ++i) {
        int flag = 1;
        for (int j = 0; j < sz; ++j) {
            if (releax(j)) {
                flag = 0;
            }
        }
        if (flag) return false;
    }
    for (int i = 0; i < sz; ++i) {
        if (releax(i)) return true;
    }
    return false;
}

int main()
{
    int T;
    cin >> T;
    while (T--) {
        // 初始化
        es.clear();
        memset(dist, 0x3f, sizeof(dist));
        memset(vis, 0, sizeof(vis));
        cin >> n >> m;
        dist[1] = 0;
        vis[1] = 1;
        int u, v, w;
        for (int i = 0; i < m; ++i) {
            cin >> u >> v >> w;
            if (w >= 0) {
                edge tmpe1 = { u, v, w };
                edge tmpe2 = { v, u, w };
                es.push_back(tmpe1);
                es.push_back(tmpe2);
            }
            else {
                edge tmpe = { u, v, w };
                es.push_back(tmpe);
            }
        }
        if (bellman()) cout << "YES" << endl;
        else cout << "NO" << endl;
    }

    return 0;
}

posted on 2022-02-27 23:01  Black_x  阅读(84)  评论(0编辑  收藏  举报