关于差分约束
定义
差分约束系统
差分约束系统 是一种特殊的 \({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;
}
都看到这了,真的不点个赞吗(>ω<*)