• 浅谈差分约束系统


    前置芝士:SPFA判负环

    对于差分约束系统,就是来解决一系列类似下面的问题:

    差分约束系统是一种特殊的(N)元一次不等式组,它包含(N)个变量(X_1...X_n)以及(M)个约束条件。每个约束条件都是由两个变量做差构成的。

    我们要求的是,一组解(X_1=a,X_2=b...)满足所有的约束条件。

    对于每一个不等式,我们都可以变形成(X_i-X_j<=C)的形式,而移项之后,我们会发现它与最短路算法的松弛操作很相似。也就是说,我们可以从(j)(i)连一条边长为(C)的有向边,跑最短路。

    对于形如(X_i-X_j>=C)的形式,两边乘以(-1)即可。

    对于建出来的图,如果存在负环则无解,否则(X_i=dis[i])就是一组解。

    对于判负环,那显然就要用到我们的(Spfa)了。

    例1:(luogu) (P1993)

    显然是一道差分约束的裸题。这是最直接的模型,直接跑即可。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<queue>
    #include<cstring>
    #define MAXN 10001<<1
    using namespace std;
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-')w=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9'){
    		s=(s<<1)+(s<<3)+(ch^48);
    		ch=getchar();
    	}
    	return s*w;
    }
    struct edge{
    	int nxt,to,dis;
    }e[MAXN<<1];
    int head[MAXN<<1],dis[MAXN],opt,a,b,c;
    int vis[MAXN],n,m,tot,cnt[MAXN];
    queue<int>q;
    inline void add(int x,int y,int w){
    	e[++tot].to=y;
    	e[tot].nxt=head[x];
    	e[tot].dis=w;
    	head[x]=tot;
    }
    bool Spfa(int s){
    	memset(dis,0x3f,sizeof(dis));
    	dis[s]=0;q.push(s);vis[s]=1;
    	while(!q.empty()){
    		int k=q.front();
    		q.pop();vis[k]=0;
    		cnt[k]++;
    		if(cnt[k]>=n)return 0;
    		for(int i=head[k];i;i=e[i].nxt){
    			int j=e[i].to;
    			if(dis[j]>dis[k]+e[i].dis){
    				dis[j]=dis[k]+e[i].dis;
    				if(!vis[j])vis[j]=1,q.push(j);
    			}
    		}
    	}
    	return 1;
    }
    bool spfa(int s){
    	vis[s]=1;
    	for(int i=head[s];i;i=e[i].nxt){
    		int j=e[i].to;
    		if(dis[j]<dis[s]+e[i].dis){
    			dis[j]=dis[s]+e[i].dis;
    			if(vis[j])return 0;
    			if(!spfa(j))return 0;
    		}
    	}
    	vis[s]=0;
    	return 1;
    }
    int main(){
    	n=read(),m=read();
    	for(int i=1;i<=m;++i){
    		opt=read(),a=read(),b=read();
    		if(opt==1){
    			c=read();//Xi-Xj>=c
    			add(b,a,c);
    		}
    		else if(opt==2){
    			c=read();
    			add(a,b,-c);
    		}
    		else if(opt==3){
    			add(a,b,0);
    			add(b,a,0);
    		}
    	}
    	for(int i=1;i<=n;++i)add(0,i,0),dis[i]=-0x3f;
    	if(!spfa(0))printf("No
    ");
    	else printf("Yes
    ");
    	return 0;
    } 
    

    练习题:

    1.(luogu) (P4878)

    2.(luogu) (P3275)

  • 相关阅读:
    方法返回值使用哪个关键字?
    Java中带参数的方法和JavaScript中带参数的函数有什么不同?
    如何调用方法
    Java中如何声明方法?JavaScript中如何声明函数?
    为什么编程语言中要有方法
    什么叫方法.
    说说字符常量和字符串常量的区别
    什么是JDK?什么是JRE?说说它们之间的区别?
    Go 错误处理
    Go Defer
  • 原文地址:https://www.cnblogs.com/h-lka/p/11360211.html
Copyright © 2020-2023  润新知