[负环问题解法]使用spfa和bellman-ford

终于开始认真对待图论了

因为听说一直是提高组的,动得很少,直到现在机房打提高的氛围下,开始学一些皮毛的东西

模板题目链接

这是一道求负环的题目,照理来说大家都是用spfa来判断负环的

但是我觉得bellman-ford更优

并且在这个模板题目中,spfa开O2过,bellman不开O2还比spfa快?

为什么呢?

因为

关于spfa

——他死了

(所以机房基本所有人转dijistra了)

但是dijistra无法解决负环问题

因此选择bellman和spfa(队列优化的bellman)

其实还可以用其他方法过掉,比如

SPFA他死了算法

思路

因为出现负环的时候会一直循环循环循环……

然后TLE

所以在原版spfa上加一个cnt数组记录一个点出队的次数

如果出队次数大于点数,就说明一定出现负环了

因此加给判断就可以了

题外话

之前xzjds给我讲了邻接表储存,但是后来发现其实广泛叫做链式前向星而不是叫做邻接表……

如果不会的话可以百度

要储存边的话还可以用向量容器和玄学结构体(将会在bellman里使用)

代码

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define memset0(a) memset(a,0,sizeof a)
#define memset1(a) memset(a,127,sizeof a)
#define N 500005
using namespace std;
int tot,m,n,s;
int ne[N], la[N], link[N], co[N], dis[N];
int cnt[N];//important
bool vis[N];
inline int read() {
    int f = 1, x = 0; char ch;
    do { ch = getchar(); if (ch == ‘-‘)f = -1; } while (ch<‘0‘ || ch>‘9‘);
    do { x = x * 10 + ch - ‘0‘; ch = getchar(); } while (ch >= ‘0‘&&ch <= ‘9‘);
    return f * x;
}
void add(int x, int y, int z)
{
    tot++; ne[tot] = y; co[tot] = z; la[tot] = link[x]; link[x] = tot;
}
bool spfa(int s)
{
    memset1(dis);
    memset0(vis);
    memset0(cnt);

    queue<int>q;
    q.push(s);
    vis[s] = true;
    dis[s] = 0;
    while (!q.empty())
    {
        int now = q.front();
        q.pop();
        vis[now] = false;//?
        if (cnt[now] >= n) return true;
        for (int k = link[now]; k; k = la[k])
        {
            if (dis[ne[k]] > dis[now] + co[k])
            {
                dis[ne[k]] = dis[now] + co[k];
                if (vis[ne[k]] == false)
                {
                    q.push(ne[k]);
                    vis[ne[k]] = true;
                    cnt[ne[k]]++;
                    if (cnt[ne[k]] >= n) return true;
                }
            }
        }
    }
    return false;
}
int main()
{
    int T=read();
    while (T--)
    {
        memset0(link);
        n = read(), m = read();
        s = 1; tot = 0;
;        for (int i = 1; i <= m; i++)
        {
            int x=read(), y=read(), z=read();
            add(x, y, z);
            if (z >= 0) add(y, x, z);
        }
        if(spfa(s))puts("YE5");
        else puts("N0");
    }
    return 0;
}

spfa

是的,不加O2会TLE。只有90分。

由于本蒟蒻不会优化,因此学习了更好的bellman判断负环

Bellman-ford算法

思路

可以把dis数组一开始都设为0

先全部松弛操作一遍(relaxing一遍)

然后再去松弛,如果能松弛,就是有负环

这个相对spfa来说,当数据点数小的时候,时间是比spfa快的

当然如果RP不好spfa速度会更快

为什么每次都有题外话

用的边的储存方式是从大佬@Planet6174 看来的

感觉非常玄学但是很容易使用

代码

#include<bits/stdc++.h>
#define N 500005
using namespace std;
int tot,m,n,s;
int dis[N];
inline int read() {
    int f = 1, x = 0; char ch;
    do { ch = getchar(); if (ch == ‘-‘)f = -1; } while (ch<‘0‘ || ch>‘9‘);
    do { x = x * 10 + ch - ‘0‘; ch = getchar(); } while (ch >= ‘0‘&&ch <= ‘9‘);
    return f * x;
}
struct eg{
    int u,v,w;
    eg(int u = 0, int v = 0, int w = 0) : u(u), v(v), w(w) {}
} edge[N];
bool bellman_ford()
{
    memset(dis,0,sizeof(dis));
    for(int i=1;i<=n-1;i++)
        for(int j=1;j<=m;j++)
            if (dis[edge[j].u] + edge[j].w < dis[edge[j].v])
                dis[edge[j].v] = dis[edge[j].u] + edge[j].w;
    for (int j = 1; j <= m; j++)
        if (dis[edge[j].u] + edge[j].w < dis[edge[j].v])
            return true;
    return false;

}
int main()
{
    int T=read();
    while (T--)
    {
        n = read(), m = read();
;        for (int i = 1; i <= m; i++)
        {
            edge[i].u=read(), edge[i].v=read(), edge[i].w=read();
            edge[i].u--; edge[i].v--;
            if (edge[i].w >= 0) {
                ++i; ++m; edge[i] = eg(edge[i - 1].v, edge[i - 1].u, edge[i - 1].w);
            }
        }
        if(bellman_ford()) puts("YE5");
        else puts("N0");
    }
    return 0;
}

bellman-ford

就是这样了

原文地址:https://www.cnblogs.com/fsy2017/p/9860875.html

时间: 2024-10-10 20:16:32

[负环问题解法]使用spfa和bellman-ford的相关文章

POJ 3259 Wormholes【最短路/SPFA判断负环模板】

农夫约翰在探索他的许多农场,发现了一些惊人的虫洞.虫洞是很奇特的,因为它是一个单向通道,可让你进入虫洞的前达到目的地!他的N(1≤N≤500)个农场被编号为1..N,之间有M(1≤M≤2500)条路径,W(1≤W≤200)个虫洞.FJ作为一个狂热的时间旅行的爱好者,他要做到以下几点:开始在一个区域,通过一些路径和虫洞旅行,他要回到最开时出发的那个区域出发前的时间.也许他就能遇到自己了:).为了帮助FJ找出这是否是可以或不可以,他会为你提供F个农场的完整的映射到(1≤F≤5).所有的路径所花时间都

ACM: POJ 3259 Wormholes - SPFA负环判定

POJ 3259 Wormholes Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Description While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way pa

uva558 Wormholes SPFA 求是否存在负环

J - Wormholes Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description In the year 2163, wormholes were discovered. A wormhole is a subspace tunnel through space and time connecting two star systems. Wormholes

spfa判断负环

会了spfa这么长时间竟然不会判断负环,今天刚回.. [例题]poj3259 题目大意:当农场主 John 在开垦他的农场时,他发现了许多奇怪的昆虫洞.这些昆虫洞是单向的,并且可以把你从入口送到出口,并且使得时间倒退一段时间. John 的每个农场包含 N(1≤N≤500)块地,编号从 1-N,这 N 块地之间有 M(1≤M≤2500)条路. W(1≤W≤200)个昆虫洞.因为 John 是一个狂热的时间旅行迷,他想尝试着做这样一件事:从某块地出发,通过一些路径和昆虫洞,返回到出发点,并且时间早

poj3259 Wormholes --- spfa判负环

又写了个bellman模板一直RE求解啊... #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <map> #define inf 0x

(简单) LightOJ 1074 Extended Traffic,SPFA+负环。

Description Dhaka city is getting crowded and noisy day by day. Certain roads always remain blocked in congestion. In order to convince people avoid shortest routes, and hence the crowded roads, to reach destination, the city authority has made a new

POJ 3259 虫洞旅行 spfa判负环

Wormholes Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 31425   Accepted: 11431 Description While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way p

【模板】负环(spfa)

洛谷——P3385 [模板]负环 题目描述 暴力枚举/SPFA/Bellman-ford/奇怪的贪心/超神搜索 输入输出格式 输入格式: 第一行一个正整数T表示数据组数,对于每组数据: 第一行两个正整数N M,表示图有N个顶点,M条边 接下来M行,每行三个整数a b w,表示a->b有一条权值为w的边(若w<0则为单向,否则双向) 输出格式: 共T行.对于每组数据,存在负环则输出一行"YE5"(不含引号),否则输出一行"N0"(不含引号). 输入输出样例

bzoj1690:[Usaco2007 Dec]奶牛的旅行(分数规划+spfa判负环)

前段时间准备省选没更,后段(?)时间省选考砸没心情更,最近终于开始恢复刷题了... 题目大意:有n个点m条有向边的图,边上有花费,点上有收益,点可以多次经过,但是收益不叠加,边也可以多次经过,但是费用叠加.求一个环使得收益和/花费和最大,输出这个比值. 显然这就是经典的分数规划题啊,就是最优比率环,那么就二分答案,将所有边(u,v)的边权改为[v的点权-(u,v)原边权*mid],这可以算是最优比率环的公式了吧,然后判一下是否有正环,有的话就说明答案可行.判正环有够别扭的,那就全部改成相反数然后