关于差分约束

定义

差分约束系统

差分约束系统 是一种特殊的 \({n}\) 元一次不等式组,它包含 n 个变量 \({x_1,x_2,\dots,x_n}\) 以及 m 个约束条件,每个约束条件是由两个其中的变量做差构成的,形如 \({x_i-x_j\leq c_k}\),其中 \({1 \leq i, j \leq n, i \neq j, 1 \leq k \leq m}\) 并且 \({c_k}\) 是常数(可以是非负数,也可以是负数)。我们要解决的问题是:求一组解 \({x_1=a_1,x_2=a_2,\dots,x_n=a_n}\),使得所有的约束条件得到满足,否则判断出无解。

差分约束系统中的每个约束条件 \({x_i-x_j\leq c_k}\) 都可以变形成 \({x_i\leq x_j+c_k}\),这与单源最短路中的三角形不等式 \({dist[y]\leq dist[x]+z}\) 非常相似。因此,我们可以把每个变量 \({x_i}\) 看做图中的一个结点,对于每个约束条件 \({x_i-x_j\leq c_k}\),从结点 \({j}\) 向结点 \({i}\) 连一条长度为 \({c_k}\) 的有向边。

注意到,如果 \({\{a_1,a_2,\dots,a_n\}}\) 是该差分约束系统的一组解,那么对于任意的常数 \({d,\{a_1+d,a_2+d,\dots,a_n+d\}}\) 显然也是该差分约束系统的一组解,因为这样做差后 \({d}\) 刚好被消掉。

以上摘自OIWIKI

翻译

简单来说,差分约束系统就是一种\({N}\)元一次不等式组,它包含\({N}\)个变量\({X_1 \sim X_N}\)以及\({M}\)个约束条件,它的基本结构形如
                        \({X_i - X_j \leq C_k}\)(其中\({C_k}\)为常数)

而我们的任务即是求一组解(误解的话当然就是吴姐的啦

而对于每一个约束条件(不等式) 
                        \({X_i - X_j \leq C_k}\)

都可以变形为      
                        \({X_i \leq X_j + C_k}\)

这与最短路的三角形不等式

                        \({dis[y]\leq dis[x]+z}\)

不能说是一模一样,只能说是完全相同
于是我们就可以把所有约束条件的变形 看成是从\({j}\)出发向\({i}\)连一条边权为\({C_k}\)的有向边
那么求不等式组的解 即为求这个图的最短路
于是我们得出
求差分约束的解<=>求图的最短路

例题

luogu P1993 小K的农场

思路

题意 转化 连边
\({x_a - x_b \geq c}\) \({x_b - x_a \leq -c}\) add(a,b,-c)
\({x_a - x_b \leq c}\) \({x_a - x_b \leq c}\) add(b,a,c)
\({x_a = x_b }\) \({x_a - x_b \leq 0,x_b - x_a \leq 0}\) add(b,a,0),add(a,b,0)

利用spfa判断负环,如果不存在输出Yes,存在输出No

Elaina's code

Elaina's code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x3f
#define INF 0x3f3f3f3f
#define mst(a,b) memset(a,b,sizeof(a))
#define Elaina 0
const int N = 30100;
int n,m;
int dis[N],tot,head[N];
bool vis[N];
struct EDGE{
	int nxt,w,to;
}e[N];
void add(int x, int y, int z)
{
	e[++tot].to=y;
	e[tot].nxt=head[x];
	e[tot].w=z;
	head[x]=tot;
}

void spfa(int x){
	vis[x]=1;
	for(int i=head[x];i;i=e[i].nxt){
		int k=e[i].to;
		if(dis[k]>dis[x]+e[i].w){
			if(vis[k]){
				cout<<"No";
				exit(0);
			}
			dis[k]=dis[x]+e[i].w;
			spfa(k);
		}
	}
	vis[x]=0;
}

signed main(){
	cin>>n>>m;
	int s,x,y,z;
	for(int i=1;i<=m;i++){
		cin>>s;
		if(s==1){
			cin>>x>>y>>z;
			add(x,y,-z);
		}else if(s==2){
			cin>>x>>y>>z;
			add(y,x,z);
		}else{
			cin>>x>>y;
			add(x,y,0);
			add(y,x,0);
		}
	}
	for(int i=1;i<=n;i++){
		add(0,i,0);
	}
	mst(dis,INF);
	dis[0]=0;
	spfa(0);
	cout<<"Yes";
	return Elaina;
}

都看到这了,真的不点个赞吗(>ω<*)

posted @ 2024-03-11 15:35  Elaina_0  阅读(16)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end