差分约束学习笔记
先放出来例题传送门
给出一组包含 \(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\) 即可。