1. 程式人生 > >[負環問題解法]使用spfa和bellman-ford

[負環問題解法]使用spfa和bellman-ford

ash def 說明 一個點 是的 while lane display getchar()

終於開始認真對待圖論了

因為聽說一直是提高組的,動得很少,直到現在機房打提高的氛圍下,開始學一些皮毛的東西

模板題目鏈接

這是一道求負環的題目,照理來說大家都是用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

就是這樣了

[負環問題解法]使用spfa和bellman-ford