zoukankan      html  css  js  c++  java
  • Spfa【p3385】【模板】负环(spfa)

    z

    你没有发现两个字里的blog都不一样嘛 qwq

    题目描述

    毒瘤数据要求判负环

    分析:

    还是融合了不少题解的思想的。

    负环定义:
    权值和为负的环

    //在网络上并没有找到一个官方定义,暂且这么理解。

    SPFA:
    支持负边权的情况.

    spfa是最短路算法.如果一个环上的边权有负的,我们可以重复走这条路来获得更小的边权,所以这可以作为我们使用spfa判断负环的根据

    //如果一个位置入队次数不小于n次,那它一定位于环上,所以这可以作为我们的判断标准。

    听说STL的队列比较慢,换掉!

    但是如果手打队列的话

    队尾指针队首指针一直++根本停不下来怎么办?

    我们可以重复使用!

    像这样↓

    //这三行代码只是演示,并不是程序中这样写。
    
    if(l>n)l=0;
    if(r>n)r=0;
    if(l<0)l=n;
    
    //因为r一直增加或者重置为0,所以没必要判断r<0
    

    考虑把dis值大的放在最下边,dis值小的放在最上边。

    因为我们取出的位置一直是向前的,所以把dis值小的放在最下边

    (也不能说是最下边,即放在刚刚取过的位置处,再比较一下与原位置dis值大小.)

    至于为什么尽量去取dis值小的?

    因为这些被更新过的点dis值小,我们可能是通过一条负边到达的此节点,我们再去对它更新一下,可以尽可能早的判断出负环.

    //偷懒的话应该可以用优先队列来做,不过没有尝试,留给您了!

    坑:

    YE5是5!!! N0是0!!!

    ----------------AC代码-----------------

    // 3106ms 改数据之前 (话说我也不知道啥时候改的.
    //改数据之后 吸氧 571ms qwq.
    // Creator: 顾z
    // Date:2018.08.29
    //------------------------------------------------
    #include<bits/stdc++.h>
    #define IL inline
    #define RI register int
    #define N 100086
    #define clear(a) memset(a,0,sizeof a)
    #define rk for(RI i=1;i<=n;i++)
    IL void read(int &x){
        int f=1;x=0;char s=getchar();
        while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
        while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
        x*=f;
    }
    int n,m,T,s;
    struct code{int u,v,w;}edge[N<<1];
    bool vis[N];
    int head[N],tot,dis[N],cnt[N],q[N];
    IL void add(int x,int y,int z){edge[++tot].u=head[x];edge[tot].v=y;edge[tot].w=z;head[x]=tot;}
    IL bool spfa(int s)
    {
    	int l,r;
        l=r=0;
       	memset(dis,0x3f,sizeof dis);
        clear(vis);clear(cnt);clear(q);
        vis[s]=true;cnt[s]=1;dis[s]=0;
        q[r++]=s;
        while(l!=r)
    	{
            int u=q[l++];
            if(l>n)l=0;//重复使用
    		vis[u]=false;
            for(RI i=head[u];i;i=edge[i].u)
            {
                if(dis[edge[i].v]>dis[u]+edge[i].w)
                {    
                    dis[edge[i].v]=dis[u]+edge[i].w;
                    cnt[edge[i].v]=cnt[u]+1;//题解思想.
                    if(cnt[edge[i].v]>=n and edge[i].w<0)
                    	return true;
                     //这里需要判断一下边权是否为负。
                     //因为看到讨论区的一组hack数据,所以尝试改一下,
                     //然后就过啦~~~
                    if(!vis[edge[i].v])
    				 {
                        vis[edge[i].v]=true;
                        if(dis[edge[i].v]>dis[q[l]])
                        {
                        	l--;
    						if(l<0)
    							l=n;//重复使用
    						q[l]=edge[i].v;
                        }
                        else
                        {
                        	q[r++]=edge[i].v;
    						if(r>n)
    							r=0;//重复使用
                        }
                    }
                }
            }
        }
        return false;
    }
    int main()
    {
    	read(T);
    	while(T--)
    	{
    		s=1,tot=0;clear(head);
        	read(n),read(m);
        	for(RI i=1,u,v,w;i<=m;i++)
        	{
        		read(u),read(v),read(w);
            	add(u,v,w);
            	if(w>=0)add(v,u,w);
    		}
           	puts(spfa(s)?"YE5":"N0");
    	}
    }
    

    写在后面

    //这份代码并没有考虑多个连通图中的负环情况

    //因此依旧可以被hack掉.

    //可能 正确性 or 内容是错误的

    //提供参考啦~~

    UPD

    --2018.09.24.

    为啥这个题的数据点改了 emmm

    撤了10个毒瘤数据点?

    本人尝试了深搜spfa,依旧被卡.

    码了一通宽搜spfa.

    4355ms 卡过?

    没了毒瘤数据点,感觉这题还是比较好做 emmm.

    所以说为啥要改数据 QAQ

    这里放一下代码.

    注意边权有0的情况,也要建双向边.

    //否则只有90pts qwq.

    ----------------(bfs)spfa----------------

    //没有吸氧 4355ms. qwq
    //吸氧 1138ms. qwq
    #include<bits/stdc++.h>
    #define IL inline
    #define RI register int
    #define N 100086
    #define clear(a) memset(a,0,sizeof a)
    #define rk for(RI i=1;i<=n;i++)
    using namespace std;
    IL void read(int &x)
    {
        int f=1;x=0;char s=getchar();
        while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
        while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
        x*=f;
    }
    int n,m,T;
    struct code{int u,v,w;}edge[N];
    bool vis[N];
    int head[N],tot,dis[N],cnt[N];
    IL void add(int x,int y,int z){edge[++tot].u=head[x];edge[tot].v=y;edge[tot].w=z;head[x]=tot;}
    IL bool spfa(int now)
    {
        rk vis[i]=false,dis[i]=2147483647,cnt[i]=false;
        queue<int>q;
        q.push(now);
    	vis[now]=true;
    	dis[now]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();vis[u]=false;
            if(cnt[u]>=n)return true;
            for(RI i=head[u];i;i=edge[i].u)
            {
                if(dis[edge[i].v]>dis[u]+edge[i].w)
                {
                    dis[edge[i].v]=dis[u]+edge[i].w;
                    if(!vis[edge[i].v])
                    {
                        q.push(edge[i].v);
                        vis[edge[i].v]=true;
                        cnt[edge[i].v]++;
                        if(cnt[edge[i].v]>=n)return true;
                    }
                }
            }
        }
        return false;
    }
    int main()
    {
        read(T);
        while(T--)
        {
        	read(n),read(m);
        	tot=0;clear(head);
        	for(RI i=1,u,v,w;i<=m;i++)
        	{
        		read(u),read(v),read(w);
            	if(w<0)add(u,v,w);
            	else add(u,v,w),add(v,u,w);
            }
            puts(spfa(1)?"YE5":"N0");
        }
    }
    
  • 相关阅读:
    并查集
    CCF201604-02
    作业八
    作业七
    数据结构--链队列基本操作
    数据结构--栈的基本操作
    eclipse配置maven
    软件架构
    scala lambda 表达式 & spark RDD函数操作
    spark(3)
  • 原文地址:https://www.cnblogs.com/-guz/p/9616676.html
Copyright © 2011-2022 走看看