虫洞

链接

https://www.acwing.com/problem/content/906/

题目

农夫约翰在巡视他的众多农场时,发现了很多令人惊叹的虫洞。

虫洞非常奇特,它可以看作是一条 单向 路径,通过它可以使你回到过去的某个时刻(相对于你进入虫洞之前)。

农夫约翰的每个农场中包含N片田地,M条路径(双向)以及W个虫洞。

现在农夫约翰希望能够从农场中的某片田地出发,经过一些路径和虫洞回到过去,并在他的出发时刻之前赶到他的出发地。

他希望能够看到出发之前的自己。

请你判断一下约翰能否做到这一点。

下面我们将给你提供约翰拥有的农场数量F,以及每个农场的完整信息。

已知走过任何一条路径所花费的时间都不超过10000秒,任何虫洞将他带回的时间都不会超过10000秒。

输入格式
第一行包含整数F,表示约翰共有F个农场。

对于每个农场,第一行包含三个整数N,M,W。

接下来M行,每行包含三个整数S,E,T,表示田地S和E之间存在一条路径,经过这条路径所花的时间为T。

再接下来W行,每行包含三个整数S,E,T,表示存在一条从田地S走到田地E的虫洞,走过这条虫洞,可以回到T秒之间。

输出格式
输出共F行,每行输出一个结果。

如果约翰能够在出发时刻之前回到出发地,则输出“YES”,否则输出“NO”。

数据范围
1≤F≤5
1≤N≤500,
1≤M≤2500,
1≤W≤200,
1≤T≤10000,
1≤S,E≤N
输入样例:

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

输出样例:

NO
YES

思路

虫洞可以理解为负权边,问题就是判断图中是否存在负环。那么就要用spfa算法。
spfa求负环常用方法两种:
  1.(cnt[u])记录节点入队的次数,当某个节点入队次数n次,说明该节点在负环当中。
  2.(cnt[u])记录节点所在的最短路路径中的节点数,如果节点数大于n,说明该节点在负环中。
一般来说用第二种的效率更高。
众所周知,spfa随便都能被卡,然后还有各种优化:
  1.将spfa的队列改成栈
  2.如果spfa被卡,说明图中有大概率存在负环,需要加入一个变量表示经验值,记录所有节点的入队次数和(count),当(count>2n)时表示发现负环
  3.SLF优化,视情况可以用一下
  4.当求在整个图中是否存在负环,需要将所有点加入队列,所有点的距离可以初始化为0(不算优化
以及:如果出题人能够写出在有负权图中比spfa跑的更快的算法,请随便ban!

这道题中判断整个图中的负环,所以一开始需要将说有点加入队列中再跑spfa。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=510,M=6000;
int h[N],e[M],w[M],nex[M],idx,n;
int d[N],q[N],st[N],cnt[N];
bool spfa(){
    int hh=0,tt=0;
    for(int i=1;i<=n;++i){
        q[tt++]=i;
        st[i]=1;
        d[i]=0;
        cnt[i]=0;
    }
    while(hh!=tt){
        int u=q[--tt];//栈优化
        //int u=q[hh++];
        //if(hh==N) hh=0;
        st[u]=0;
        for(int i=h[u];~i;i=nex[i]){
            int v=e[i];
            if(d[v]>d[u]+w[i]){
                d[v]=d[u]+w[i];
                cnt[v]=cnt[u]+1;
                if(cnt[v]>=n) return false;
                if(!st[v]){
                    q[tt++]=v;
                    if(tt==N) tt=0;
                    st[v]=1;
                }
            }
        }
    }
    return true;
}
void add(int u,int v,int t){
    e[idx]=v;
    w[idx]=t;
    nex[idx]=h[u];
    h[u]=idx++;
}
int main(){
    
    int m1,m2,T;
    scanf("%d",&T);
    while(T--){
        memset(h,-1,sizeof h);idx=0;
        scanf("%d%d%d",&n,&m1,&m2);
        while(m1--){
            int x,y,t;
            scanf("%d%d%d",&x,&y,&t);
            add(x,y,t);
            add(y,x,t);
        }
        while(m2--){
            int x,y,t;
            scanf("%d%d%d",&x,&y,&t);
            add(x,y,-t);
        }
        if(spfa()) puts("NO");
        else puts("YES");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/jjl0229/p/12760888.html