• SPFA判负环模板


    题目描述

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

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

    输入格式

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

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

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

    接下来 mmm 行,每行三个整数 u,v,wu, v, wu,v,w。

    • w≥0w geq 0w0,则表示存在一条从 uuu 至 vvv 边权为 www 的边,还存在一条从 vvv 至 uuu 边权为 www 的边。
    • w<0w < 0w<0,则只表示存在一条从 uuu 至 vvv 边权为 www 的边。

    输出格式

    对于每组数据,输出一行一个字符串,若所求负环存在,则输出 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

    负环即边权和为负数的环。
    重要性质:两点之间存在负环时,必然没有最短路。(因为路径越更新越小
    原理:lyd的书上有言:从起点到x的最短路包含了>=n条边,说明肯定有节点被重复经过了,故最短路径上存在环且环上各个点能更新下一个点的dist值。
    设cnt[x]表示从1到x的最短路包含的边数,当更新d[y]=d[x]+z时也同时更新cnt[y]=cnt[x]+1。当有cnt[y]>=n时直接退出即可。
    #include <bits/stdc++.h>
    #define N 2005
    #define M 3005
    using namespace std;
    int n,m,tot=0;
    int head[N],edge[2*M],ver[2*M],Next[2*M],cnt[N],d[N];
    bool v[N];
    void add(int x,int y,int z)
    {
        ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
    }
    bool spfa()
    {
        queue<int>q;
        d[1]=0,v[1]=1,cnt[1]=0;
        q.push(1);
        while(q.size())
        {
            int x=q.front();
            q.pop();
            v[x]=0;
            int i;
            for(i=head[x];i;i=Next[i])
            {
                int y=ver[i],z=edge[i];
                if(d[y]>d[x]+z)
                {
                    d[y]=d[x]+z;
                    cnt[y]++;
                    if(cnt[y]>=n)return 1;
                    if(!v[y])q.push(y),v[y]=1;
                }
            }
        }
        return 0;
    }
    int main()
    {
        int t;
        cin>>t;
        while(t--)
        {
            memset(head,0,sizeof(head));
            memset(edge,0,sizeof(edge));
            memset(ver,0,sizeof(ver));
            memset(Next,0,sizeof(Next));
            memset(cnt,0,sizeof(cnt));
            memset(v,0,sizeof(v));
            memset(d,0x3f,sizeof(d));
            tot=0;
            cin>>n>>m;
            int i,u,v,w;
            for(i=1;i<=m;i++)
            {
                scanf("%d%d%d",&u,&v,&w);
                add(u,v,w);
                if(w>=0)add(v,u,w);
            }
            if(spfa())cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
        return 0;
    }



  • 相关阅读:
    Winform 异步更新listbox
    Object-C
    易学易懂
    C# login with cookie and fiddler2
    开源 侧滑 和 Tab滑动翻页 控件
    Mysql Java type mapping
    jQuery滑动导航菜单
    js判断是移动端还是pc端
    设为主页和加入收藏
    原生javascript效果:无缝滚动
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/12746433.html
Copyright © 2020-2023  润新知