洛谷 P3385 [模板] 负环 题解 SPFA

【模板】负环

题目描述

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

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

输入格式

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

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

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

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

  • w ≥ 0 w \geq 0 w0,则表示存在一条从 u u u v v v 边权为 w w w 的边,还存在一条从 v v v u u u 边权为 w w w 的边。
  • w < 0 w < 0 w<0,则只表示存在一条从 u u u v v v 边权为 w w w 的边。

输出格式

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

样例 #1

样例输入 #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 ≤ n ≤ 2 × 1 0 3 1 \leq n \leq 2 \times 10^3 1n2×103 1 ≤ m ≤ 3 × 1 0 3 1 \leq m \leq 3 \times 10^3 1m3×103
  • 1 ≤ u , v ≤ n 1 \leq u, v \leq n 1u,vn − 1 0 4 ≤ w ≤ 1 0 4 -10^4 \leq w \leq 10^4 104w104
  • 1 ≤ T ≤ 10 1 \leq T \leq 10 1T10
提示

请注意, m m m 不是图的边数。

原题

洛谷P3385——传送门

代码

#include <bits/stdc++.h>
using namespace std;
#define max_Heap(x) priority_queue<x, vector<x>, less<x>>
#define min_Heap(x) priority_queue<x, vector<x>, greater<x>>
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<long long, long long> PLL;
const double PI = acos(-1);

const int maxn = 2e3 + 6;
const int maxm = 3e3 + 6;

int n, m; // 点的个数,有向边的个数

struct edge
{
    int to, len; // to为边的指向,len为边的长度即边权
};

vector<edge> e[maxn]; // 存储以点i为起点的边

ll minDis[maxn]; // 从起点到第i个点的最短路长度
bool vis[maxn];  // 第i个点是否在队列中
int cnt[maxn];   // 记录某个点的入队次数,若次数大于等于n,存在负环

bool spfa()
{
    memset(minDis, 0x3f, sizeof(minDis));
    memset(vis, 0x00, sizeof(vis));
    memset(cnt, 0x00, sizeof(cnt));
    minDis[1] = 0; // 按照题目要求,以一号点作为起点
    queue<int> q;
    q.push(1);
    vis[1] = 1;
    cnt[1]++;
    while (!q.empty())
    {
        int st = q.front();
        q.pop();
        vis[st] = 0;          // 已出队,vis赋值0
        for (edge eg : e[st]) // 遍历以st为起点的所有有向边
        {
            int ed = eg.to;
            int weight = eg.len;
            if (minDis[st] + weight < minDis[ed]) // 判断是否可以更新最短路长度
            {
                minDis[ed] = minDis[st] + weight;
                if (!vis[ed]) // 如果已经在队列中,则不需要再加入队列
                {
                    q.push(ed);
                    vis[ed] = 1;      // 记录在队列中
                    cnt[ed]++;        // 加入队列的次数++
                    if (cnt[ed] >= n) // 入队次数>=n,存在负环,返回0
                    {
                        return 0;
                    }
                }
            }
        }
    }

    return 1; // 不存在负环,返回1
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int t;
    cin >> t;
    while (t--)
    {
        for (int i = 1; i < maxn; i++)
        {
            e[i].clear();
        }
        cin >> n >> m;
        int u, v, w; // 从u到v的权值为w的有向边
        while (m--)
        {
            cin >> u >> v >> w;
            if (w < 0)
                e[u].push_back({v, w});
            else
            {
                e[u].push_back({v, w});
                e[v].push_back({u, w});
            }
        }

        if (spfa())
            cout << "NO" << '\n';
        else
            cout << "YES" << '\n';
    }

    return 0;
}
  • 18
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于洛谷上的p1036题目,我们可以使用Python来解决。下面是一个可能的解法: ```python def dfs(nums, target, selected_nums, index, k, sum): if k == 0 and sum == target: return 1 if index >= len(nums) or k <= 0 or sum > target: return 0 count = 0 for i in range(index, len(nums)): count += dfs(nums, target, selected_nums + [nums[i]], i + 1, k - 1, sum + nums[i]) return count if __name__ == "__main__": n, k = map(int, input().split()) nums = list(map(int, input().split())) target = int(input()) print(dfs(nums, target, [], 0, k, 0)) ``` 在这个解法中,我们使用了深度优先搜索(DFS)来找到满足要求的数列。通过递归的方式,我们遍历了所有可能的数字组合,并统计满足条件的个数。 首先,我们从给定的n和k分别表示数字个数和需要选取的数字个数。然后,我们输入n个数字,并将它们存储在一个列表nums中。接下来,我们输入目标值target。 在dfs函数中,我们通过迭代index来选择数字,并更新选取的数字个数k和当前总和sum。如果k等于0且sum等于target,我们就找到了一个满足条件的组合,返回1。如果index超出了列表长度或者k小于等于0或者sum大于target,说明当前组合不满足要求,返回0。 在循环中,我们不断递归调用dfs函数,将选取的数字添加到selected_nums中,并将index和k更新为下一轮递归所需的值。最终,我们返回所有满足条件的组合个数。 最后,我们在主程序中读入输入,并调用dfs函数,并输出结果。 这是一种可能的解法,但不一定是最优解。你可以根据题目要求和测试数据进行调试和优化。希望能对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值