适用问题

差分约束算法主要解决求不等式组解的问题
将数学问题建立一个图论模型利用最短路解决
首先看一组不等式方程:
给出一组包含 mm 个不等式,有 nn 个未知数的形如:

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

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

算法核心

上述不等式移项可得:xcmxcm+ymx_{c_m}\leqslant x_{c'_m}+y_m
类似于最短路中的三角形不等式:dist[y]dist[x]+wxydist[y] \leqslant dist[x]+w_{x\rightarrow y}
所以我们可以转换为求解最短路的问题
xcmx_{c'_m} 引一条权为 ymy_m 的边到 xcmx_{c_m}
因为需要引用到单源最短路,所以我们需要建立一个超级源点
使其与每个点都连通,且与每个点的距离均设为 0
这样求出来的由超级源点到各点的最短路即为一组解
但什么时候为无解的情况 ?
即整个图中出现 负环 的情况
若存在负环,则每次松弛后都会将最短路缩短一次
最后得出来的结果就是负无穷,显然无解

算法实现

根据给出的不等式建图
任取除了 1 ~ n 的点作为超级源点,连边
由于可能存在负权边,所以使用 spfa 算法求解最短路,顺便判断负环
最后求出最短路即可
P5960 【模板】差分约束算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+15;
const int inf=0x3f3f3f3f;
int n,m;
struct Edge{
int to;
int next;
int w;
}edge[N];
int cnt=1;
int head[N];
void add(int u,int v,int w){
edge[cnt].to=v;
edge[cnt].next=head[u];
edge[cnt].w=w;
head[u]=cnt++;
}
int dist[N],vis[N],inq[N];
bool ok=true;
void spfa(int x){
memset(dist,inf,sizeof(dist));
vis[x]=1;
dist[x]=0;
inq[x]++;
queue<int> q;
q.push(x);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(dist[v]>dist[u]+edge[i].w){
dist[v]=dist[u]+edge[i].w;
if(!vis[v]){
vis[v]=1;
inq[v]++;
q.push(v);
if(inq[v]>n){ok=false;return;}
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
//建立超级源点
for(int i=1;i<=n;i++) add(0,i,0);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&v,&u,&w);
add(u,v,w);
}
spfa(0);
if(ok==false){printf("NO");return 0;}
for(int i=1;i<=n;i++) printf("%d ",dist[i]);
return 0;
}

技巧&细节

求非负解

先取最短路中的最小值,再逐一减去这个值,得到的即为非负解

1
2
3
int minn=inf;
for(int i=1;i<=n;i++) minn=min(minn,dist[i]);
for(int i=1;i<=n;i++) printf("%d ",dist[i]-minn);

转换不等式

如果有 xcmxcmymx_{c_m}-x_{c'_m} \geqslant y_m
可以同时乘 -1 得到相反的不等式再加边
xcm=xcmx_{c_m}=x_{c'_m}则等价于 xcmxcm+0x_{c_m}\leqslant x_{c'_m}+0xcmxcm+0x_{c'_m}\leqslant x_{c_m}+0