• vijos1053 用spfa判断是否存在负环


    MARK 用spfa判断是否存在负环

    判断是否存在负环的方法有很多,

    其中用spfa判断的方法是:如果存在一个点入栈两次,那么就存在负环。

    细节想想确实是这样,按理来说是不存在入栈两次的如果边权值为正的话

    这个算法是O(N*M)

    还有一种方法是直接用bellman-ford,虽说spfa也就是bellman-ford+FIFO队列

    而且bellman-ford还可以计算负环的值

    顺手附上代码好了:

    for(int i=0;i<n;i++) d[i]=INF;//初始化
    d[0]=0;
    
    for(int k=0;k<n-1;k++)//迭代n-1次,目前不懂为什么
        for(int i=0;i<m;i++){//检查每条边
            int x=u[i],y=v[i];
            if(d[x]<INF) d[y]<?=d[x]+w[i];
    }
    

     这一题我是没有用bellman-ford...因为看到有人说用这个超时了= =

    这里说一下用spfa的做法= =

    虽然本蒟蒻第6个点莫名WA,但是毕竟思路还是正确的,自己MARK一下

    目测是一些地方没有考虑到吧,据说有重边?有可能是这个?= =懒得管了

    其实就是每个点都spfa一遍,看是否存在负环;

    如果不存在的的话,就输出s-这些点的距离

    至于spfa里面,其实也很简单,就是

    	while(!q.empty()){
    		int u=q.front();
    		q.pop();
    		vis[u]=false;
    		for(int i=head[u];i!=-1;i=e[i].next){
    			int v=e[i].to;
    			if(dist[v]>dist[u]+e[i].w){
    				dist[v]=dist[u]+e[i].w;
    				if(!vis[v]){
    					vis[v]=true;
    					q.push(v);
    					if(ans[v]<2){//这里用数组ans记录下,v这个点入栈几次
    						ans[v]++;
    					}
    					else return true;
    				}
    			}
    		}
    	}
    

    附上完整代码:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<iostream>
    using namespace std;
    const int maxn=101000;
    int n,m,k,t,x,y,s,z,tot=0;
    struct edge{
    	int from,to,w,next;
    }e[1010000];
    int head[maxn],dist[maxn],ans[maxn];
    bool vis[maxn];
    int f[maxn];
    bool flag;
    void add(int x,int y,int z){
    	e[tot].from=x;
    	e[tot].to=y;
    	e[tot].w=z;
    	e[tot].next=head[x];
    	head[x]=tot++;
    }
    bool spfa(int s){
    	queue<int>q;
    	memset(dist,63,sizeof(dist));
    	memset(vis,false,sizeof(vis));
    	memset(ans,0,sizeof(ans));
    	q.push(s);
    	dist[s]=0;
    	while(!q.empty()){
    		int u=q.front();
    		q.pop();
    		vis[u]=false;
    		for(int i=head[u];i!=-1;i=e[i].next){
    			int v=e[i].to;
    			if(dist[v]>dist[u]+e[i].w){
    				dist[v]=dist[u]+e[i].w;
    				if(!vis[v]){
    					vis[v]=true;
    					q.push(v);
    					if(ans[v]<2){
    						ans[v]++;
    					}
    					else return true;
    				}
    			}
    		}
    	}
    	return false;
    }
    int main(){
    	freopen("data.txt","r",stdin);
    	scanf("%d%d%d",&n,&m,&s);
    	memset(head,-1,sizeof(head));
    	for(int i=1;i<=m;i++){
    		scanf("%d%d%d",&x,&y,&z);
    		add(x,y,z);
    		if(x==y && z<0){
    			printf("-1
    ");
    			return 0;
    		}
    	}	
        for(int i=1;i<=n;i++){
        	if(spfa(i)){
        		printf("-1
    ");
        		return 0;
        	}
        }
        spfa(s);
        for(int i=1;i<=n;i++){
        	if(dist[i]>1000000){
        		if(i!=s) printf("NoPath
    ");
        		else printf("0
    ");
        	}
        	else printf("%d
    ",dist[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    Linux中常用的关机和重新启动命令
    Ubuntu 7.10 J2EE开发环境lomboz+eclipse3.2.1+tomcat5.5.25+mysql5.
    Linux+Apache+JSP+PHP+MySQL
    SMSLib的配置
    Python正则表达式操作指南
    Ubuntu学习笔记:通过ssh远程登录服务器
    C语言文件读写:语言初学者入门讲座 第十六讲 文件
    linuxfopenfclosefreadfwrite等函数
    linux 下 scp 的用法
    wget3
  • 原文地址:https://www.cnblogs.com/polebug/p/3907847.html
Copyright © 2020-2023  润新知