差分约束学习笔记

先放出来例题传送门

给出一组包含 \(m\) 个不等式,有 \(n\) 个未知数的形如:

\[\begin{cases} x_{c_1}-x_{c'_1}\leq y_1 \\x_{c_2}-x_{c'_2} \leq y_2 \\ \cdots\\ x_{c_m} - x_{c'_m}\leq y_m\end{cases} \]

的不等式组,求任意一组满足这个不等式组的解。

因为 \(\le\) 的存在,我们很难通过常规数学方法解决这道题目。这时,我们就要运用到差分约束的方法解决。

看到 \(x_i-x_j\le y\),变形为 \(x_i\le x_j+y\),可以联想到最短路松弛的条件 \(dis_v\le dis_u+w\)。所以我们可以从 \(j\)\(i\) 连一条长度为 \(y\) 的边,建一个超级源点 \(0\),跑一遍最短路,\(x_i=dis_i\) 即为方程的一组可行解。

但众所周知,有负权环的图是没有最短路的。所以跑的时候还要判是否有负权环,有则无解。

然后就可以写出代码:

点击查看代码
int n,m,dis[N],cnt[N];
int tot,head[N];struct node{int to,nxt,cw;}e[N];
inline void add(int u,int v,int w){e[++tot]={v,head[u],w},head[u]=tot;}
queue<int> q;
void solve(){
	scanf("%d%d",&n,&m);
	for(int i=1,u,v,w;i<=m;i++){
		scanf("%d%d%d",&v,&u,&w);
		add(u,v,w);
	}
	for(int i=1;i<=n;i++)dis[i]=infi,add(0,i,0);
	q.push(0);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(dis[u]+e[i].cw<dis[v]){
				dis[v]=dis[u]+e[i].cw;
				cnt[v]++;
				if(cnt[v]>n)printf("NO"),exit(0);
				q.push(e[i].to);
			}
		}
	}
	for(int i=1;i<=n;i++)printf("%d ",dis[i]);
}
signed main(){
	int t=1;
//	scanf("%d",&t);
	while(t--)solve();
}

拓展:

  • \(x_i-x_j\ {\color{red}\ge}\ y\) 将两边同时乘上 \(-1\),变为 \(x_j-x_i\le -y\),即从 \(i\)\(j\) 连一条长度为 \(-y\) 的边。

  • \(x_i=x_j\) 变为 \(x_i-x_j\le 0,x_j-x_i\le 0\) 即可。

posted @ 2023-01-06 15:58  yinhee  阅读(42)  评论(2)    收藏  举报