差分约束系统
如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj ≤ k
的不等式(i,j∈[1,n]
,k
为常数),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
问题分析
我们对不等式进行简单的变形:
a
i
−
a
j
≤
k
⟹
a
i
≤
k
+
a
j
a_i-a_j\leq k\ \ \ \Longrightarrow \ \ \ a_i\leq k+a_j
ai−aj≤k ⟹ ai≤k+aj
我们将
a
i
a_i
ai看作从起点到i
点的最短距离,将
k
k
k看作由j
点到i
的边的权值。此时,求解差分约束系统就转化成了求解单源最短路的问题。
为了便于求解,我们设立一个新的结点
a
0
a_0
a0,然后建立
a
0
a_0
a0指向每个结点的权值为0的边。
我们以
a
0
a_0
a0为源点,求解源点到各个结点的最短距离,这些最短距离就构成了差分约束系统的一组解。
算法细节
由于边权值可正可负,所以我们不能采用迪克斯特拉算法。因此,我们采用SPFA算法。
由于 a 0 a_0 a0到各个节点的初始结点的距离为0,所以得到的解中的每个变量的取值一定 ≤ 0 \leq0 ≤0。
P5960 【模板】差分约束算法
#include<iostream>
#include<queue>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
class EDGE{
public:
int to;
int w;
public:
EDGE(int to,int w){
this->to=to;
this->w=w;
}
};
vector<EDGE>graph[5005];
int n,m;
int a,b,y;
int ans[5005];
int cnt[5005];
bool SPFA(int s){
memset(ans,127,sizeof(ans));
memset(cnt,0,sizeof(cnt));
queue<EDGE>q;
ans[s]=0;
q.push(EDGE(s,0));
cnt[s]++;
while(!q.empty()){
EDGE e=q.front();
q.pop();
int pos=e.to;
for(int i=0;i<graph[pos].size();i++){
int to=graph[pos][i].to;
int w=graph[pos][i].w;
if(ans[to]>ans[pos]+w){
ans[to]=ans[pos]+w;
q.push(EDGE(to,ans[to]));
cnt[to]++;
if(cnt[to]>=n) return 0;
}
}
}
return 1;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a>>b>>y;
graph[b].push_back(EDGE(a,y));
}
for(int i=1;i<=n;i++) graph[0].push_back(EDGE(i,0));
if(SPFA(0)){
for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
}
else cout<<"NO";
return 0;
}