• 负环与差分约束


     之前觉得自己写的负环不是很好,今天想再写一遍。

    负环

     负环,又叫负权回路,负权环,指的是一个图中存在一个环,里面包含的边的边权总和 < 0 \lt0 <0。在存在负环的图中,是求不出最短路径的,因为只要在这个环上不停的兜圈子,最短路径就会无限小。

     定理:如果一个点的入队的次数大于点的总数 N N N,则存在负权回路。
     若图中没有负环,则最多经过 n − 1 n-1 n1轮迭代后算法结束。所以若第 n n n轮迭代仍有结点的最短路能被更新,则图中有负环。
     所以我们用 c n t [ x ] cnt[x] cnt[x]表示 1 1 1 x x x的最短路包含的边数(该题要求以 1 1 1作为源点), c n t [ 1 ] = 0 cnt[1]=0 cnt[1]=0。每次用 d i s [ x ] + w ( x , y ) dis[x]+w(x,y) dis[x]+w(x,y)更新 d i s [ y ] dis[y] dis[y]时,也是 c n t [ x ] + 1 cnt[x]+1 cnt[x]+1更新 c n t [ y ] cnt[y] cnt[y]。此过程中若出现 c n t [ y ] ≥ n cnt[y]≥n cnt[y]n,则图中有负环。最坏情况复杂度也是 O ( n m ) O(nm) O(nm)

     参考代码

    #include
    #define in read()
    #define MAXN 3003
    #define MAXM 2*MAXN
    using namespace std;
    int T,n,m;
    int nex[MAXM],first[MAXM],to[MAXM],val[MAXM],tot=0;
    bool vis[MAXN];
    int cnt[MAXN],dis[MAXN]; 
    queue<int>q;
    
    inline int read(){
    	int x=0,f=1;char c=getchar();
    	while(c<'0' or c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0' and c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    	return x*f;
    }
    
    inline void addedge(int u,int v,int w){//建图
    	nex[++tot]=first[u];
    	first[u]=tot;
    	to[tot]=v;
    	val[tot]=w;
    }
    
    inline void prework(){//初始化
    	memset(first,0,sizeof(first));
    	memset(to,0,sizeof(to));
    	memset(nex,0,sizeof(nex));
    	memset(val,0,sizeof(val));
    	tot=0;
    }
    
    inline bool spfa(){//spfa求负环
    	memset(vis,0,sizeof(vis));
    	memset(dis,0x3f3f3f3f,sizeof(dis));//初始化距离为极大值
    	memset(cnt,0,sizeof(cnt));
    	dis[1]=0;vis[1]=true;
    	q.push(1);
    	while(!q.empty()){
    		int u=q.front();q.pop();//取出队首元素并出队
    		vis[u]=false;//标记节点不在队列之中
    		for(int e=first[u];e;e=nex[e]){
    			int v=to[e];
    			if(dis[v]>dis[u]+val[e]){
    				dis[v]=dis[u]+val[e];
    				cnt[v]=cnt[u]+1;
    				if(cnt[v]>=n)return true;//判断是否为负环
    				if(!vis[v]){
    					q.push(v);//入队
    					vis[v]=true;//标记节点在队列之中
    				}
    			}
    		}
    	}
    	return false;
    }
    
    int main(){
    	T=in;
    	while(T--){
    		prework();
    		n=in;m=in;
    		for(int i=1;i<=m;i++){
    			int u=in,v=in,w=in;
    			addedge(u,v,w);
    			if(w>=0)addedge(v,u,w);
    		}
    		if(spfa())cout<<"YES"<<'\n';
    		else cout<<"NO"<<'\n';
    	}
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    推荐练习:
     1.P3305[模板]负环
     2.P2850 [USACO06DEC]Wormholes G

     顺便把差分约束也学了把

    差分约束系统(好玄学)

    差分约束系统是一种特殊的 n n n元一次不等式组,它包含 n n n个变量 x 1 , x 2 , x 3 , . . . , x n x_1,x_2,x_3,...,x_n x1,x2,x3,...,xn以及 m m m个约束条件,每个约束条件是由两个其中的变量做差构成的,形如 x i − x j ≤ c k x_i-x_j\le c_k xixjck,其中 1 ≤ i , j ≤ n , i 不等于 j , 1 ≤ k ≤ m 1\le i,j\le n,i不等于j,1\le k\le m 1i,jn,i不等于j,1km并且 c k c_k ck是常数(可以是非负数也可以是负数)。我们要解决的问题是:求一组解 x 1 = a 1 , x 2 = a 2 , . . . , x n = a n x_1=a_1,x_2=a_2,...,x_n=a_n x1=a1,x2=a2,...,xn=an ,使得所有的约束条件得到满足,否则判断出无解。

     差分约束系统中的每个约束条件 x i − x j ≤ c k x_i-x_j\le c_k xixjck都可以变形成 x i ≤ x j + c k x_i\le x_j+c_k xixj+ck,这与单源最短路中的三角形不等式 d i s y ≤ d i s x + v a l ( x , y ) dis_y\le dis_x+val(x,y) disydisx+val(x,y)非常相似。因此,我们可以把每个变量 x i x_i xi看做图中的一个结点,对于每个约束条件 x i − x j ≤ c k x_i-x_j\le c_k xixjck,从结点 j j j向结点 i i i连一条长度为 c k c_k ck的有向边。

     注意到,如果 a 1 , a 2 , a 3 , . . . , a i {a_1,a_2,a_3,...,a_i} a1,a2,a3,...,ai是该差分约束系统的一组解,那么对于任意的常数 d d d a 1 + d , a 2 + d , a 3 + d , . . . , a i + d {a_1+d,a_2+d,a_3+d,...,a_i+d} a1+d,a2+d,a3+d,...,ai+d显然也是该差分约束系统的一组解,因为这样做差后 d d d刚好被消掉。

     设 d i s 0 = 0 dis_0=0 dis0=0并向每一个点连一条权重为 0 0 0边,跑单源最短路,若图中存在负环,则给定的差分约束系统无解,否则, x i = d i s i x_i=dis_i xi=disi为该差分约束系统的一组解。

     一般使用 S P F A SPFA SPFA判断图中是否存在负环,最坏时间复杂度为 O ( n m ) O(nm) O(nm)

     参考代码

    #include
    #define in read()
    #define MAXM 20005
    #define re register
    using namespace std;
    
    bool vis[MAXM];
    int cnt[MAXM],dis[MAXM];
    int n,m;
    int nex[MAXM],first[MAXM],to[MAXM],val[MAXM],tot=0;
    queue<int>q;
    
    inline int read(){
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    	return x*f;
    }
    
    inline void addedge(int u,int v,int w){
    	nex[++tot]=first[u];
    	first[u]=tot;
    	to[tot]=v;
    	val[tot]=w;
    }
    
    inline bool spfa(int st){
    	for(re int i=1;i<=n;i++)dis[i]=0x3f3f3f3f;
    	q.push(st),dis[st]=0,vis[st]=1;
    	while(!q.empty()){
    		int u=q.front();q.pop();vis[u]=0;
    		for(int e=first[u];e;e=nex[e]){
    			int v=to[e];
    			if(dis[v]>dis[u]+val[e]){
    				dis[v]=dis[u]+val[e];
    				if(!vis[v]){
    					cnt[v]=cnt[u]+1;
    					if(cnt[v]>n+1)return true;
    					q.push(v);
    					vis[v]=1;
    				}
    			}
    		}
    	}
    	return false;
    }
    
    int main(){
    	n=in,m=in;
    	for(re int i=1;i<=m;i++){
    		int x=in,y=in,k=in;
    		addedge(y,x,k);
    	}
    	for(re int i=1;i<=n;i++)
    		addedge(0,i,0);
    	if(spfa(0))cout<<"NO"<<'\n';
    	else{
    		for(re int i=1;i<=n;i++)
    		cout<<dis[i]<<" "; 
    	} 
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    推荐练习:
     1.P5960 【模板】差分约束算法
     2.P1260 工程规划

  • 相关阅读:
    pdf只要其中一页 pdf只要第一页怎么办 pdf只要前几页怎么弄
    macbook桌面文件丢失
    【超实用】3 分钟,教你用 Docker 部署一个 Python 应用!
    【Spring Boot】使用XML配置文件实现数据库操作(一)
    java计算机毕业设计高校大学生就业系统MyBatis+系统+LW文档+源码+调试部署
    brew 常用命令
    3000帧动画图解MySQL为什么需要binlog、redo log和undo log
    IntelliJ IDEA 2022创建Maven项目
    ​GOPS演讲 | 如何构建现代运营与支持体系,实现团队的高效协同
    10 个 Python set 常用操作函数!,oppoPython 面试题
  • 原文地址:https://blog.csdn.net/Lucas_FC_/article/details/126329435