差分约束系统
线性规划
在通用线性规划问题中,我们通常给定一个矩阵 A A A,和一个未知向量 → x \underset{x}{\rightarrow} x→,和一个已知向量 → b \underset{b}{\rightarrow} b→。求解矩阵不等式 A x ≤ b Ax \leq b Ax≤b。有时候我们并不关心目标函数,而是希望计算出一个可行解(或告知无解)。
差分约束系统
在一个差分约束系统中,线性规划矩阵 A A A每一个都有且仅有一个1和-1,其他位置均为0。因此,由 A x ≤ b Ax \leq b Ax≤b所给出的差分约束系统是由m个约束条件,n个变量的差额限制条件,其中每一个约束条件都是类似于下面的不等式 x i − x j ≤ b k x_{i} - x_{j} \leq b_{k} xi−xj≤bk,其中m为A的行数,n为A的列数, 1 ≤ i , j ≤ n , i ≠ j 1 \leq i,j \leq n,i \neq j 1≤i,j≤n,i=j并且 1 ≤ k ≤ m 1 \leq k \leq m 1≤k≤m。
定理:如果 x x x是差分约束系统的一个可行解,那么对于任意整数 d d d,则向量 x + d i x+di x+di同样是差分约束系统的一个可行解,其中 i i i为全部元素都是1的向量。
将d加到每一个 x i x_{i} xi上面,对于约束条件做减法的时候,d就会被消掉,因此加上一个常数向量还是一个可行解。
约束图
给定差分约束系统,其对应的约束图是有向图 G = ( V , E ) G=(V,E) G=(V,E), V = ( v 0 , v 1 , ⋯ , v n ) V=(v_{0},v_{1},\cdots,v_{n}) V=(v0,v1,⋯,vn), E = { ( v i , v j ) : x j − x i ≤ b k } ∪ { ( v 0 , v 1 ) , ( v 0 , v 2 ) , ⋯ , ( v 0 , v k ) } E=\{ (v_{i},v_{j}):x_{j} - x_{i} \leq b_{k} \} \cup \{ (v_{0},v_{1}) , (v_{0},v_{2}) , \cdots ,(v_{0},v_{k})\} E={(vi,vj):xj−xi≤bk}∪{(v0,v1),(v0,v2),⋯,(v0,vk)}。
对于权值函数 w w w的定义, w 0 j = 0 , w i j = b k w_{0j} = 0,w_{ij} = b_{k} w0j=0,wij=bk。下面的定义将把差分约数系统和图论最短路建立关系。
定理:差分约数系统的一个可行解为, x = ( δ ( v 0 , v 1 ) , δ ( v 0 , v 2 ) , ⋯ , δ ( v 0 , v n ) ) x=(\delta(v_{0},v_{1}),\delta(v_{0},v_{2}),\cdots,\delta(v_{0},v_{n})) x=(δ(v0,v1),δ(v0,v2),⋯,δ(v0,vn))
证明:考虑任意一条边 ( v i , v j ) (v_{i},v_{j}) (vi,vj),根据三角不等式有, δ ( v 0 , v j ) ≤ δ ( v 0 , v i ) + w i j \delta(v_{0},v_{j}) \leq \delta(v_{0},v_{i}) + w_{ij} δ(v0,vj)≤δ(v0,vi)+wij,即 δ ( v 0 , v j ) − δ ( v 0 , v i ) ≤ w i j \delta(v_{0},v_{j}) - \delta(v_{0},v_{i}) \leq w_{ij} δ(v0,vj)−δ(v0,vi)≤wij,又因为 x i = δ ( v 0 , v i ) , x j = δ ( v 0 , v j ) x_{i} = \delta(v_{0},v_{i}),x_{j} = \delta(v_{0},v_{j}) xi=δ(v0,vi),xj=δ(v0,vj),因此 x j − x i ≤ w i j = b k x_{j} - x_{i} \leq w_{ij} = b_{k} xj−xi≤wij=bk。
定理:如果约束图存在负权环,则差分约束系统无解。
证明:反证法,详情查看《算法导论》。
求解差分约数系统
可以选择运行一遍BellmanFord算法,也可以进行SPFA优化。
注意条件转化,在求解 d n dn dn数组的时候,注意到初始化不能设置成最大的数组,否则相加的时候会出现溢出的问题。
以下是Bellman-Ford判断负环算法。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define FR freopen("in.txt", "r", stdin)
struct Edge
{
int from;
int to;
ll w;
} e[20005];
ll dn[6000];
int tot = 0;
int n, m;
inline void add(int u, int v, ll w)
{
tot++;
e[tot].from = u;
e[tot].to = v;
e[tot].w = w;
}
int main()
{
cin >> n >> m;
while (m--)
{
int op, u, v;
cin >> op >> u >> v;
if (op == 1)
{
ll c;
cin >> c;
add(u, v, -c);
}
else if (op == 2)
{
ll c;
cin >> c;
add(v, u, c);
}
else if (op == 3)
{
add(u, v, 0);
add(v, u, 0);
}
}
for (int i = 1; i <= n; i++)
{
add(0, i, 0);
dn[i] = 1000000007;
}
dn[0] = 0;
// negi loop
for (int cnt = 0; cnt < n; cnt++)
{
for (int i = 1; i <= tot; i++)
{
Edge curr = e[i];
if (dn[curr.to] > dn[curr.from] + curr.w)
{
dn[curr.to] = dn[curr.from] + curr.w;
}
}
}
for (int i = 1; i <= tot; i++)
{
Edge curr = e[i];
if (dn[curr.to] > dn[curr.from] + curr.w)
{
cout << "No";
return 0;
}
}
cout << "Yes";
return 0;
}