差分约束系统,其实是一个类似网络流的,求一类不等式方程组的解的算法。
基本形式
差分约束系统主要用求这样的不等式方程组的一个解:
其中 。
我们先考虑其中的一条不等式 ,很明显可以转化为 。
观察这个等式,很容易联想到最短路中,点 和点 中间如果有一条长度为 的边,那么这两个点之间的最短路长度一定小于等于 。所以,我们可以把这 个方程转换为 条边,建出一个有向图来。
建完图后,我们考虑加一个大源点 ,向每一个未知数连一条长度为 的边。这样,以 作为源点跑出来的最短路,就是整个方程组的解了。
不过注意到,如果有负环,那么最短路是跑不出来的,这意味着方程组无解。
模板题代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const long long S=10000005,MS=100005;
int n,m;
int esum,to[S],nxt[S],c[S],h[MS];
int dis[MS],vis[MS];
bool inq[MS];
inline void add(int x,int y,int w)
{
to[++esum]=y;
c[esum]=w;
nxt[esum]=h[x];
h[x]=esum;
}
inline bool slove()
{
dis[0]=0;
for(int i=1;i<=n;i++)
{
dis[i]=1e9;
}
memset(vis,0,sizeof(vis));
memset(inq,0,sizeof(inq));
queue<int> q;
q.push(0);
inq[0]=true;
while(!q.empty())
{
int u=q.front();
q.pop();
inq[u]=false;
vis[u]++;
if(vis[u]>n)
{
return false;
}
for(int i=h[u];i;i=nxt[i])
{
int v=to[i],w=c[i];
if(dis[u]+w<dis[v])
{
dis[v]=dis[u]+w;
if(!inq[v])
{
inq[v]=true;
q.push(v);
}
}
}
}
return true;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
add(y,x,w);
}
for(int i=1;i<=n;i++)
{
add(0,i,0);
}
bool can=slove();
if(!can)
{
puts("NO");
}
else
{
for(int i=1;i<=n;i++)
{
printf("%d ",dis[i]);
}
printf("\n");
}
return 0;
}
一些变形
-
遇到 ,可以反过来连边,变成 ,或者同时乘以 ,将符号反过来
-
遇到 ,可以转化为两个不等式: 和
-
遇到 的和为 这类问题时,可以用前缀和转换为