知识点 - 差分约束系统
解决问题类型:
背景是给你若干个不等式,形如 x i − x j ≤ b xi−xj≤b xi−xj≤b,需要你判断x的解的存在性或是最优解
而差分约束系统即为这个问题转化为一个图论问题,进而跑最短路来判环或求最值距离(最优解)。
这里转化的原理是三角不等式,即
d
(
v
)
−
d
(
u
)
≤
c
o
s
t
(
u
,
v
)
d(v)−d(u)≤cost(u,v)
d(v)−d(u)≤cost(u,v),可以建为一条花费为
c
o
s
t
(
u
,
v
)
cost(u,v)
cost(u,v)的边
(
u
,
v
)
(u,v)
(u,v)。
复杂度:
O ( n 2 ) O(n^2) O(n2)
实现
-
与最短路之间的联系
先给出结论:求解差分约束系统,都可以转化成图论的单源最短路径(或最长路径)问题。
我们观察上面例子中的不等式,都是 x [ i ] − x [ j ] < = a [ k ] x[i] - x[j] <= a[k] x[i]−x[j]<=a[k],可以进行移项,成为 x [ i ] < = x [ j ] + a [ k ] x[i] <= x[j] + a[k] x[i]<=x[j]+a[k],我们令 a [ k ] = w ( j , i ) a[k] = w(j, i) a[k]=w(j,i), d i s [ i ] = x [ i ] dis[i]=x[i] dis[i]=x[i],并使 i = v , j = u i=v,j=u i=v,j=u,那么原始就变为: d i s [ u ] + w ( u , v ) > = d i s [ v ] dis[u]+w(u,v)>=dis[v] dis[u]+w(u,v)>=dis[v],于是可以联想到最短路模型中的一部分代码
if(dis[u]+w(u,v)<=dis[v]){ dis[v]=dis[u]+w(u,v); }
这不正与松弛操作相似吗?
但是好像不等号方向刚好相反,但其实这并不矛盾
上面的代码要实现的是使 d i s [ u ] + w ( u , v ) > d i s [ v ] dis[u]+w(u,v)>dis[v] dis[u]+w(u,v)>dis[v],而对于不等式,我们进行建边的操作:对于每个不等式 x [ i ] − x [ j ] < = a [ k ] x[i] - x[j] <= a[k] x[i]−x[j]<=a[k],对结点 j j j 和 i i i 建立一条 j − > i j -> i j−>i的有向边,边权为 a [ k ] a[k] a[k],求 x [ n − 1 ] − x [ 0 ] x[n-1] - x[0] x[n−1]−x[0] 的最大值就是求 0 到n-1的最短路,两者刚好吻合。所以求解差分约束问题就转化为了最短路问题。
-
问题解的存在性
由于在求解最短路时会出现存在负环或者终点根本不可达的情况,在求解差分约束问题时同样存在
(1)、存在负环
如果路径中出现负环,就表示最短路可以无限小,即不存在最短路,那么在不等式上的表现即 X [ n − 1 ] − X [ 0 ] < = T X[n-1] - X[0] <= T X[n−1]−X[0]<=T中的T无限小,得出的结论就是 X [ n − 1 ] − X [ 0 ] X[n-1] - X[0] X[n−1]−X[0]的最大值不存在。在SPFA实现过程中体现为某一点的入队次数大于节点数。(貌似可以用 ( n u m n o d e ) \sqrt{(num_{node})} (numnode)来代替减少运行时间)
(2)、终点不可达
这种情况表明 X [ n − 1 ] X[n-1] X[n−1]和 X [ 0 ] X[0] X[0]之间没有约束关系, X [ n − 1 ] − X [ 0 ] X[n-1] - X[0] X[n−1]−X[0]的最大值无限大,即 X [ n − 1 ] X[n-1] X[n−1]和 X [ 0 ] X[0] X[0]的取值有无限多种。在代码实现过程中体现为 d i s [ n − 1 ] = I N F dis[n-1]=INF dis[n−1]=INF。
-
不等式转换
做题时可能会遇到不等式中的符号不相同的情况,但我们可以对它们进行适当的转化
(1)方程给出: X [ n − 1 ] − X [ 0 ] > = T X[n-1]-X[0]>=T X[n−1]−X[0]>=T ,可以进行移项转化为: X [ 0 ] − X [ n − 1 ] < = − T X[0]-X[n-1]<=-T X[0]−X[n−1]<=−T。
(2)方程给出: X [ n − 1 ] − X [ 0 ] < T X[n-1]-X[0]<T X[n−1]−X[0]<T, 可以转化为 X [ n − 1 ] − X [ 0 ] < = T − 1 X[n-1]-X[0]<=T-1 X[n−1]−X[0]<=T−1。
(3)方程给出: X [ n − 1 ] − X [ 0 ] = T X[n-1]-X[0]=T X[n−1]−X[0]=T,可以转化为 X [ n − 1 ] − X [ 0 ] < = T & & X [ n − 1 ] − X [ 0 ] > = T X[n-1]-X[0]<=T\&\&X[n-1]-X[0]>=T X[n−1]−X[0]<=T&&X[n−1]−X[0]>=T,再利用(1)进行转化即可
-
应用
对于不同的题目,给出的条件都不一样,我们首先需要关注问题是什么,如果需要求的是两个变量差的最大值,那么需要将所有不等式转变成"<=“的形式,建图后求最短路;相反,如果需要求的是两个变量差的最小值,那么需要将所有不等式转化成”>=",建图后求最长路。
例题
POJ 1716 Integer Intervals
HDOJ 3666 THE MATRIX PROBLEM
代码
SPFA求最短路
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn=1010;
const int inf=1e9;
struct edge{
int v,cost;
edge(int _v=0,int _cost=0):v(_v),cost(_cost) {}
};
vector<edge>E[maxn];
bool vis[maxn];
int cnt[maxn],dist[maxn],s,n;
void addedge(int u,int v,int w)
{
E[u].push_back(edge(v,w));
}
bool SPFA()
{
memset(vis,false,sizeof(vis));
memset(cnt,0,sizeof(cnt));
for ( int i=1;i<=n;i++ ) dist[i]=inf;
vis[s]=true;
dist[s]=0;
queue<int>que;
que.push(s);
cnt[s]=1;
while ( !que.empty() ) {
int u=que.front();
que.pop();
vis[u]=false;
for ( int i=0;i<E[u].size();i++ ) {
int v=E[u][i].v;
int cost=E[u][i].cost;
if ( dist[v]>dist[u]+cost ) {
dist[v]=dist[u]+cost;
if ( !vis[v] ) {
vis[v]=true;
que.push(v);
if ( ++cnt[v]>n ) return false;
}
}
}
}
return true;
}
最长路模板:即将边权取负,最后的结果取负即可