关于一个图中是否存在负环

来源:互联网 发布:米惠淘宝返利网 编辑:程序博客网 时间:2024/06/05 23:58

负环的判断

第一次写博客,表示心里很慌,dalao不喜勿喷。

我第一次接触负环的判断是在一道差分约束的题目里:点击打开链接

说实话,这类题目其实都是比较裸的判负环,但是很多人就卡在负环上,有的人会用dijkstra算法(我没有试过,但听学长说判不了,只会算个错的答案出来),有的人用SPFA,但是用的BFS,于是。。。光荣的TLE了,原因就在于在判断以及之后的退出上DFS比BFS更有力,所以要用DFS,那么具体的操作呢?

先来看看正常情况(单源最短路)下的bfs版SPFA:

//h[]初始化为INF
void SPFA(){    int he=0,t=1;//手写队列,虽然queue很好用,但我还是爱手写    h[st]=0;vis[st]=true;    d[1]=st;//d[]就是队列数组    do{//这个循环干啥的应该都懂        he++;        int p=d[he];        vis[p]=false;        for(register int i=head[p];i!=-1;i=e[i].next)//我建图一般用前向星,vector版的应该是for(int j=0;j<e[sta].size();j++)        {            int v=e[i].to;//列举下一个节点            if(h[v]>h[p]+e[i].dis)//判断谁更短            {                h[v]=h[p]+e[i].dis;                if(!vis[v]){//是否访问过该节点                    d[++t]=v;//没有则入队                    vis[v]=true;//标记                }            }        }    }while(he<t);}
那么这种算法在出现了负环的情况下会如何呢?


我们就以这个有点鬼畜的环为例,假设我们搜到了a节点,继续搜b节点,因为边权为负,所以h[a]更新,那么搜b、搜c、搜d时,因为边权都是负的,那么都会更新相应节点的h值,这个时候……我们搜回a节点了,将要发生的一切,似乎就顺理成章了,死循环到天荒地老……那么要如何避免呢?

如果你用BFS,那么判定的依据是一个点入队超过n次,这个复杂度足以让你绝望了,但是如果说是DFS,判断的依据就是多次被搜到,这个解决起来就方便多了,看一看代码:

struct node{    int next;    int to;    int dis;}e[500010];
bool vis[500001],flag;//flag标记负环void add(int u,int v,int w){        e[++tot].to=v;    e[tot].next=head[u];    e[tot].dis=w;    head[u]=tot;}void dfs(int sta){vis[sta]=true;//该点访问过了if (flag)
 return;    for (int i=head[sta];i;i=e[i].next)
{         int v=e[i].to;        if (h[sta]+e[i].dis<h[v]){            h[v]=h[sta]+e[i].dis;            if (vis[v])
{                  flag=true;//重复访问一个点,负环出现,flag改变                return;            }else            dfs(v);//其实就是把队列改成相应的深搜而已        }    }vis[sta]=false;//相当于回溯return ; }
//主函数里的调用是for(int i=1;i<=n;i++){dfs(i);if(flag)  break;}
在这里有一个优化,原版的SPFA的h[]数组初始化时是赋的INF,一个极大的数,这在单源最短路是对的,但在判负环时反而会拖慢效率,所以不如把h[]初始化为0,那么只有出现负权边时才会更新并搜索,在发现负环后立马退出,极大的提升了效率,至于别的快读之类的优化就看你个人了。

有关负环我就讲这么多,推荐一些练习:洛谷P1993 P3385 poj3259

本人萌新,dalao不喜勿喷