引入
线性规划是研究线性约束条件下线性目标函数最值的方法总称,如网络流等。OI 很少会出现只能用线性规划算法解决的问题,绝大多数这类问题可以通过网络流建模等方法更高效地解决。
举个例子:
你是一个工厂老板,仓库里有 10 吨木材 和 8 个工时 ,你可以生产两种产品:椅子 和 桌子 。
做一把椅子 :需 1 吨木材 + 2 个工时,利润 30 元 。
做一张桌子 :需 2 吨木材 + 1 个工时,利润 50 元 。
你的目标 :决定生产多少椅子 (
x 1 x_1 x 1 ) 和多少桌子 (
x 2 x_2 x 2 ),使得
总利润最大 。
把这个问题建模成 LP 就是:
Maximize Z = 30 x 1 + 50 x 2 ( 总利润 ) \begin{aligned}
\text{Maximize } & Z = 30x_1 + 50x_2 \quad (\text{总利润}) \\
\end{aligned} Maximize Z = 30 x 1 + 50 x 2 ( 总利润 )
s.t. 1 x 1 + 2 x 2 ≤ 10 ( 木材限制 ) 2 x 1 + 1 x 2 ≤ 8 ( 工时限制 ) x 1 , x 2 ≥ 0 \begin{aligned}
\text{s.t.} \quad & 1x_1 + 2x_2 \le 10 \quad (\text{木材限制}) \\
& 2x_1 + 1x_2 \le 8 \quad (\text{工时限制}) \\
& x_1, x_2 \ge 0
\end{aligned} s.t. 1 x 1 + 2 x 2 ≤ 10 ( 木材限制 ) 2 x 1 + 1 x 2 ≤ 8 ( 工时限制 ) x 1 , x 2 ≥ 0
(此处忽略掉整数限制)
我们把这个东西画到平面上会发现一些性质:
加上整数限制的 LP 问题是一类整数规划问题,而并非简单的线性规划问题(当然如果系数矩阵是全幺模矩阵,整数规划问题和线性规划问题是等价的,否则很多是 NP-Hard 的,比如背包)。
标准型
通常规定为:
maximize c T x \begin{aligned} \text{maximize} \quad & \mathbf{c}^T \mathbf{x} \\
\end{aligned} maximize c T x
subject to A x ≤ b x ≥ 0 \begin{aligned}\text{subject to} \quad & \mathbf{A} \mathbf{x} \le \mathbf{b} \\ & \mathbf{x} \ge 0 \end{aligned} subject to Ax ≤ b x ≥ 0
三种要求:目标函数,约束条件和非负限制(单纯形法的基础)。
一些常用技巧:
添加负号转化最大最小问题和约束条件的方向。
等式约束拆成两个方向相反的约束。
如果变量没有正负数限制,将他替换成两个非负变量的差值。
对偶
原问题 (Primal) 对偶问题 (Dual) Maximize c T x \mathbf{c}^T \mathbf{x} c T x Minimize b T y \mathbf{b}^T \mathbf{y} b T y A x ≤ b \mathbf{A} \mathbf{x} \le \mathbf{b} Ax ≤ b A T y ≥ c \mathbf{A}^T \mathbf{y} \ge \mathbf{c} A T y ≥ c x ≥ 0 \mathbf{x} \ge 0 x ≥ 0 y ≥ 0 \mathbf{y} \ge 0 y ≥ 0
A A A 是约束条件的系数矩阵,
x x x 是解的列向量,
b b b 是约束条件的列向量,
c c c 是目标函数的系数列向量,
A T A^T A T 表示
A A A 的转置矩阵。
原问题转对偶问题的过程一共需要 3 步:
交换目标函数和约束符号的方向。
将约束条件的系数矩阵转置。
将原目标函数的系数列向量和原约束条件的列向量交换。
举个例子
将上面的例子作为原问题建模(为方便表示系数矩阵发生转置就把条件一
x 2 x_2 x 2 的系数改成 3):
Maximize Z = 30 x 1 + 50 x 2 ( 总利润 ) \begin{aligned}
\text{Maximize } & Z = 30x_1 + 50x_2 \quad (\text{总利润}) \\
\end{aligned} Maximize Z = 30 x 1 + 50 x 2 ( 总利润 )
s.t. 1 x 1 + 3 x 2 ≤ 10 ( 木材限制 ) 2 x 1 + 1 x 2 ≤ 8 ( 工时限制 ) x 1 , x 2 ≥ 0 \begin{aligned}
\text{s.t.} \quad & 1x_1 + 3x_2 \le 10 \quad (\text{木材限制}) \\
& 2x_1 + 1x_2 \le 8 \quad (\text{工时限制}) \\
& x_1, x_2 \ge 0
\end{aligned} s.t. 1 x 1 + 3 x 2 ≤ 10 ( 木材限制 ) 2 x 1 + 1 x 2 ≤ 8 ( 工时限制 ) x 1 , x 2 ≥ 0
给问题对偶一下就变成了需要用最小代价收购木材和工时,单位木材和工时分别设置一个价格
y 1 y_1 y 1 和
y 2 y_2 y 2 ,使得老板获得的利润不能低于他卖产品获得的利润,求出每个单位木材和工时的价格。
对偶问题建模:
Minimize W = 10 y 1 + 8 y 2 ( 总花费 ) \begin{aligned}
\text{Minimize } & W = 10y_1 + 8y_2 \quad (\text{总花费}) \\
\end{aligned} Minimize W = 10 y 1 + 8 y 2 ( 总花费 )
s.t. 1 y 1 + 2 y 2 ≥ 30 ( 椅子价格 ) 3 y 1 + 1 y 2 ≥ 50 ( 桌子价格 ) y 1 , y 2 ≥ 0 \begin{aligned}
\text{s.t.} \quad & 1y_1 + 2y_2 \ge 30 \quad (\text{椅子价格}) \\
& 3y_1 + 1y_2 \ge 50 \quad (\text{桌子价格}) \\
& y_1, y_2 \ge 0
\end{aligned} s.t. 1 y 1 + 2 y 2 ≥ 30 ( 椅子价格 ) 3 y 1 + 1 y 2 ≥ 50 ( 桌子价格 ) y 1 , y 2 ≥ 0
三大定理:
弱对偶定理 :
c T x ≤ b T y \mathbf{c}^T \mathbf{x} \le \mathbf{b}^T \mathbf{y} c T x ≤ b T y
即:任意可行解的目标函数值,原问题(最大化) ≤ \le ≤ 对偶问题(最小化)。这为我们提供了上下界估算。
强对偶定理:
如果原问题和对偶问题都有界,原问题有最优解 x ∗ x^* x ∗ ,则对偶问题也有最优解 y ∗ y^* y ∗ ,且:
c T x = b T y \mathbf{c}^T \mathbf{x}= \mathbf{b}^T \mathbf{y} c T x = b T y
应用: 可以把最小化问题转化为最大化问题然后使用单纯形,或者原问题变量少,约束多,对偶完了就是变量多约束少。
互补松弛性 (Complementary Slackness):
一个可行解是最优解:对于原问题的第 i i i 个约束,如果没取到等号(松弛了),则对偶变量 y i y_i y i 必须为 0,反之亦然。
( b − A x ) ⋅ y = 0 (\mathbf{b} - \mathbf{A}\mathbf{x}) \cdot y = 0 ( b − Ax ) ⋅ y = 0
( c − A T y ) ⋅ x = 0 (\mathbf{c} - \mathbf{A}^T\mathbf{y}) \cdot x = 0 ( c − A T y ) ⋅ x = 0
应用: 原始对偶方法要用。
证明:
对任意可行解 x , y x,y x , y ,有
c ⊤ x ≤ b ⊤ y c^\top x \le b^\top y c ⊤ x ≤ b ⊤ y
考虑两者之差:
b ⊤ y − c ⊤ x = y ⊤ b − x ⊤ c = y ⊤ b − x ⊤ A ⊤ y + x ⊤ ( A ⊤ y − c ) = y ⊤ ( b − A x ) + x ⊤ ( A ⊤ y − c ) \begin{aligned}
b^\top y - c^\top x
&= y^\top b - x^\top c\\
&= y^\top b - x^\top A^\top y + x^\top(A^\top y - c) \\
&= y^\top(b - Ax) + x^\top(A^\top y - c)
\end{aligned} b ⊤ y − c ⊤ x = y ⊤ b − x ⊤ c = y ⊤ b − x ⊤ A ⊤ y + x ⊤ ( A ⊤ y − c ) = y ⊤ ( b − A x ) + x ⊤ ( A ⊤ y − c )
根据强对偶定理,两者之差为 0,又因为右边两项非负,所以推出互补松弛性。
原始-对偶方法
这个东西是解决线性规划问题的一类方法,最小费用最大流里面的 SSP 算法用的也是这个思想。
大体思路:先给出一个可行解,通过求解一系列相对简单的辅助问题,来逐步逼近原问题的最优解,对于原问题
Q 1 Q1 Q 1 :
minimize c T x \begin{aligned} \text{minimize} \quad & \mathbf{c}^T \mathbf{x} \\
\end{aligned} minimize c T x
subject to A x ≥ b x ≥ 0 \begin{aligned}\text{subject to} \quad & \mathbf{A} \mathbf{x} \ge \mathbf{b} \\ & \mathbf{x} \ge 0 \end{aligned} subject to Ax ≥ b x ≥ 0
maximize b T y \begin{aligned} \text{maximize} \quad & \mathbf{b}^T \mathbf{y} \\
\end{aligned} maximize b T y
subject to A T y ≤ c y ≥ 0 \begin{aligned}\text{subject to} \quad & \mathbf{A^T} \mathbf{y} \le \mathbf{c} \\ & \mathbf{y} \ge 0 \end{aligned} subject to A T y ≤ c y ≥ 0
根据互补松弛性,只需要找到一个可行解,使得
x T ( A T y − c ) = 0 \mathbf{x^T} \mathbf{(A^Ty-c)} = 0 x T ( A T y − c ) = 0 ,这个可行解就是最优解。
计算对偶问题紧约束的集合
I = { i : ( A T y − c ) i = 0 } I= \left\{i : (A^Ty-c)_i=0\right\} I = { i : ( A T y − c ) i = 0 }
minimize 1 T z \begin{aligned} \text{minimize} \quad & \mathbf{1}^T \mathbf{z} \\
\end{aligned} minimize 1 T z
subject to A x + z = b z ≥ 0 \begin{aligned}\text{subject to} \quad & \mathbf{A} \mathbf{x} + \mathbf{z} = \mathbf{b} \\ & \mathbf{z} \ge 0 \end{aligned} subject to Ax + z = b z ≥ 0
x i ≥ 0 ∀ i ∈ I x_i \ge 0 \quad \forall i \in I x i ≥ 0 ∀ i ∈ I
x i = 0 ∀ i ∉ I x_i = 0 \quad \forall i \notin I x i = 0 ∀ i ∈ / I
如果问题 Q3 的最优解是
0 0 0 ,说明满足互补松弛性,此时的
x x x 就是原问题最优解。
给问题
Q 3 Q3 Q 3 对偶一下成问题
Q 4 Q4 Q 4 。
maximize b T y \begin{aligned} \text{maximize} \quad & \mathbf{b}^T \mathbf{y} \\
\end{aligned} maximize b T y
subject to ∑ j a j i × y j ≤ 0 ∀ i ∈ I b T y ≤ 1 \begin{aligned}\text{subject to} \quad & \sum_j a_{ji} \times y_j \le 0 \ \quad \forall i \in I& \\ \mathbf{b}^T \mathbf{y} \le 1 \end{aligned} subject to b T y ≤ 1 j ∑ a ji × y j ≤ 0 ∀ i ∈ I
想办法利用
Q 4 Q4 Q 4 的解改进
Q 2 Q2 Q 2 的解,将问题
Q 2 Q2 Q 2 的解
y Q 2 = y Q 2 + ϵ y Q 4 y_{Q2} = y_{Q2}+\epsilon y_{Q4} y Q 2 = y Q 2 + ϵ y Q 4 ,在选取的
ϵ \epsilon ϵ 满足要求的情况下,
y Q 2 y_{Q2} y Q 2 一定是一组可行解,且目标函数值会更优。
选取
ϵ \epsilon ϵ 的具体方法式子推一推就行。
如果
ϵ = + ∞ \epsilon = +\infty ϵ = + ∞ ,那么对偶问题无界,原问题不可解。
全幺模矩阵
这个东西可以判定判定这个线性规划问题的最优解是否是整数解,这样可以把整数规划问题转化为线性规划问题来求解。
矩阵
A ∈ R m × n A\in R^{m\times n} A ∈ R m × n 称为全幺模(TU),当且仅当它的任意子矩阵(从
A A A 中取同样行列数得到的方阵)的行列式都属于
det ( ⋅ ) ∈ 0 , 1 , − 1 . \det(\cdot)\in{0, 1, -1}. det ( ⋅ ) ∈ 0 , 1 , − 1 .
对于全幺模矩阵
A ∈ Z m + n , b ∈ Z m , C ∈ Z n A \in Z^{m+n},b\in Z^m ,C\in Z^n A ∈ Z m + n , b ∈ Z m , C ∈ Z n 的线性规划问题
maximize c T x \begin{aligned} \text{maximize} \quad & \mathbf{c}^T \mathbf{x} \\
\end{aligned} maximize c T x
subject to A x ≤ b x ≥ 0 \begin{aligned}\text{subject to} \quad & \mathbf{A} \mathbf{x} \le \mathbf{b} \\ & \mathbf{x} \ge 0 \end{aligned} subject to Ax ≤ b x ≥ 0
如果有界,最优解一定是整数解。
证明思路是:
先证明:当 A A A TU 且 b b b 整数时,x ∣ A x ≤ b {x\mid Ax\le b} x ∣ A x ≤ b 的所有极点都是整数 。
再用最优解可在极点取得的性质 ,因此存在整数最优解。
严谨证明参见:https://www.luogu.com.cn/paste/xi6i8dkm。
常见的图论模型中,网络流、最短路、二分图等对应的线性规划问题的系数矩阵都是全幺模矩阵。
一个引理是如果矩阵的每一列的 1 形成一个区间那么就是全幺模,当然给这个矩阵转置一下也成立。
严格证明需要使用 Ghouila–Houri 判据,这里不展开。
单纯形法
单纯形法的时间复杂度的渐进上界是
O ( 2 n n m ) O(2^n nm) O ( 2 n nm ) 或
O ( 2 n m 2 ) O(2^n m^2) O ( 2 n m 2 ) ,其中
n n n 是变量数,
m m m 是约束数,实际使用非常快,和网络流一样就是
O ( 能过 ) O(能过) O ( 能过 ) 。
单纯形法主要是解决标准形式 的线性规划问题:
max ∑ j = 1 n c j x j s.t. ∑ j = 1 n a i j x j ≤ b i ( i = 1.. m ) , x j ≥ 0 \max\ \sum_{j=1}^n c_j x_j \quad
\text{s.t. }\sum_{j=1}^n a_{ij}x_j \le b_i\ (i=1..m),\ x_j\ge 0 max j = 1 ∑ n c j x j s.t. j = 1 ∑ n a ij x j ≤ b i ( i = 1.. m ) , x j ≥ 0
算法大体思路:
先将线性规划问题转化为标准形式,然后将 m m m 个约束作为线性方程组求解(要求 rank A=m)。
然后对于每个线性方程添加松弛变量。
找到原问题的一组可行解,然后一直执行换基操作,直到最优解不再改进。
定义
把不等式变成等式:加松弛变量
s i ≥ 0 s_i\ge 0 s i ≥ 0 。
A x + s = b Ax + s = b A x + s = b
总变量数变成
n + m n+m n + m 。选出
m m m 个变量当作
基变量 (它们对应的系数矩阵可逆),其余是
非基变量 。令非基变量全为 0,就能解出基变量的值——这就是一个
基本解 ;若基变量也都
≥ 0 \ge 0 ≥ 0 ,就是
基本可行解 ,对应一个顶点。
初始时如果
b ≥ 0 b\ge 0 b ≥ 0 ,基本可行解直接令非基变量全部为 0,基变量是
b b b 即可。
否则需要两次单纯形先求出可行解。
换基操作
把当前状态理解成一组方程(把基变量用非基变量表示):
给定一个基
B = ( B 1 , B 2 , … , B m ) B=(B_1,B_2,\dots,B_m) B = ( B 1 , B 2 , … , B m ) (大小
m m m )和非基
N = ( N 1 , N 2 , … , N n ) N=(N_1,N_2,\dots,N_n) N = ( N 1 , N 2 , … , N n ) (其余变量),把等式
A B x B + A N x N = b A_B x_B + A_N x_N=b A B x B + A N x N = b
移项:
A B x B = b − A N x N A_B x_B = b - A_N x_N A B x B = b − A N x N
x B = A B − 1 b − A B − 1 A N x N x_B = A_B^{-1}b - A_B^{-1}A_Nx_N x B = A B − 1 b − A B − 1 A N x N
记
b ˉ = A B − 1 b , A ˉ = A B − 1 A N \bar b = A_B^{-1}b,\qquad
\bar A = A_B^{-1}A_N b ˉ = A B − 1 b , A ˉ = A B − 1 A N
那么:
x B = b ˉ − A ˉ x N (D1) x_{B}=\bar b-\bar Ax_N
\tag{D1} x B = b ˉ − A ˉ x N ( D1 )
目标函数
z = c B ⊤ x B + c N ⊤ x N z=c_B^\top x_B + c_N^\top x_N z = c B ⊤ x B + c N ⊤ x N
z = c B ⊤ b ˉ + ( c N ⊤ − c B ⊤ A B − 1 A N ) x N z=c_B^\top \bar b + \Big(c_N^\top - c_B^\top A_B^{-1}A_N\Big)x_N z = c B ⊤ b ˉ + ( c N ⊤ − c B ⊤ A B − 1 A N ) x N
记
z 0 = c B ⊤ b ˉ , c ˉ ⊤ = c N ⊤ − c B ⊤ A ˉ z_0=c_B^\top\bar b,\qquad
\bar c^\top= c_N^\top - c_B^\top \bar A z 0 = c B ⊤ b ˉ , c ˉ ⊤ = c N ⊤ − c B ⊤ A ˉ
得到
z = z 0 + ∑ j ∈ N c ˉ j x j (D2) z = z_0 + \sum_{j\in N}\bar c_j x_j
\tag{D2} z = z 0 + j ∈ N ∑ c ˉ j x j ( D2 )
这里
c ˉ j \bar c_j c ˉ j 就是
检验数 (在 max 下,
c ˉ j > 0 \bar c_j>0 c ˉ j > 0 表示让
x j x_j x j 从 0 增大能让
z z z 增大)。
1. 选入基变量
最大化问题里,如果存在某个非基变量
x j x_j x j 使
c ˉ j > 0 \bar c_j>0 c ˉ j > 0 ,说明增大它可以让
z z z 变大 ⇒ 它是候选“入基”变量,选择入基变量有两种常用的好写的规则:
取检验数最大的那个(c ˉ j \bar c_j c ˉ j 最大的)。
取下标最小的那个(j j j 最小的)。
第 1 种可能会循环但是效率高,第 2 种更稳定但是效率低。
2. 选出基变量
让
x e x_e x e 增大时,基变量由
D 1 D1 D 1 变化:
x B i = b ˉ i − a ˉ i e , x e x_{B_i} = \bar b_i - \bar a_{i e},x_e x B i = b ˉ i − a ˉ i e , x e
为了保持可行性
x B i ≥ 0 x_{B_i}\ge 0 x B i ≥ 0 ,需要
如果 a ˉ i e > 0 \bar a_{ie}>0 a ˉ i e > 0 ,则 x e ≤ b ˉ i / a ˉ i e x_e \le \bar b_i/\bar a_{ie} x e ≤ b ˉ i / a ˉ i e 。
如果 a ˉ i e ≤ 0 \bar a_{ie}\le 0 a ˉ i e ≤ 0 ,则这一行不对 x e x_e x e 上界造成限制(增大 x e x_e x e 不会让该基变量变负)。
所以可行域允许的最大步长是
x e ≤ min i : a ˉ i e > 0 b ˉ i a ˉ i e x_e \le \min_{i:\bar a_{ie}>0}\frac{\bar b_i}{\bar a_{ie}} x e ≤ i : a ˉ i e > 0 min a ˉ i e b ˉ i
取达到最小值的那一行
l l l 作为出基变量
x B l x_{B_l} x B l 。这就是最小比值检验。
若所有
a ˉ i e ≤ 0 \bar a_{ie}\le 0 a ˉ i e ≤ 0 ,则
x e x_e x e 没上界,且
c ˉ e > 0 \bar c_e>0 c ˉ e > 0 会使
z → + ∞ z\to+\infty z → + ∞ ,所以是
无界 。
3. pivot(换基)
设进基变量是
x e ∈ N x_e\in N x e ∈ N ,出基变量是
x B l x_{B_l} x B l 。
x B l = b ˉ l − ∑ j ∈ N a ˉ l j x j x_{B_l}=\bar b_l-\sum_{j\in N}\bar a_{lj}x_j x B l = b ˉ l − j ∈ N ∑ a ˉ l j x j
x B l = b ˉ l − a ˉ l e x e − ∑ j ∈ N ∖ e a ˉ l j x j x_{B_l}=\bar b_l-\bar a_{le}x_e-\sum_{j\in N\setminus{e}}\bar a_{lj}x_j x B l = b ˉ l − a ˉ l e x e − j ∈ N ∖ e ∑ a ˉ l j x j
移项并除以
a ˉ l e \bar a_{le} a ˉ l e (注意 pivot 元要求
a ˉ l e > 0 \bar a_{le}>0 a ˉ l e > 0 才会被选为出基行):
x e = b ˉ l a ˉ l e − 1 a ˉ l e x B l − ∑ j ∈ N ∖ e a ˉ l j a ˉ l e x j (P0) x_e=\frac{\bar b_l}{\bar a_{le}}-\frac{1}{\bar a_{le}}x_{B_l}-\sum_{j\in N\setminus{e}}\frac{\bar a_{lj}}{\bar a_{le}}x_j
\tag{P0} x e = a ˉ l e b ˉ l − a ˉ l e 1 x B l − j ∈ N ∖ e ∑ a ˉ l e a ˉ l j x j ( P0 )
现在把
P 0 P0 P 0 代入所有其它行和目标行,使字典恢复成“基变量 = 常数 − 非基线性组合”的形式。
3.1 更新其它约束行 ( i ≠ l ) (i\neq l) ( i = l )
x B i = b ˉ i − a ˉ i e x e − ∑ j ∈ N ∖ e a ˉ i j x j x_{B_i}=\bar b_i-\bar a_{ie}x_e-\sum_{j\in N\setminus{e}}\bar a_{ij}x_j x B i = b ˉ i − a ˉ i e x e − j ∈ N ∖ e ∑ a ˉ ij x j
x B i = b ˉ i − a ˉ i e ( b ˉ l a ˉ l e − 1 a ˉ l e x B l − ∑ j ≠ e a ˉ l j a ˉ l e x j ) − ∑ j ≠ e a ˉ i j x j = ( b ˉ i − a ˉ i e b ˉ l a ˉ l e ) ⏟ b ˉ i ′ − ( − a ˉ i e 1 a ˉ l e ) ⏟ 系数对 x B l x B l − ∑ j ≠ e ( a ˉ i j − a ˉ i e a ˉ l j a ˉ l e ) ⏟ a ˉ i j ′ x j \begin{aligned}
x_{B_i}
&=\bar b_i-\bar a_{ie}\Big(\frac{\bar b_l}{\bar a_{le}}-\frac{1}{\bar a_{le}}x_{B_l}-\sum_{j\neq e}\frac{\bar a_{lj}}{\bar a_{le}}x_j\Big)
-\sum_{j\neq e}\bar a_{ij}x_j\\
&=\underbrace{\Big(\bar b_i-\bar a_{ie}\frac{\bar b_l}{\bar a_{le}}\Big)}_{\bar b'_i}
-\underbrace{\Big(-\bar a_{ie}\frac{1}{\bar a_{le}}\Big)}_{\text{系数对 }x_{B_l}}
x_{B_l}
-\sum_{j\neq e}\underbrace{\Big(\bar a_{ij}-\bar a_{ie}\frac{\bar a_{lj}}{\bar a_{le}}\Big)}_{\bar a'_{ij}}x_j
\end{aligned} x B i = b ˉ i − a ˉ i e ( a ˉ l e b ˉ l − a ˉ l e 1 x B l − j = e ∑ a ˉ l e a ˉ l j x j ) − j = e ∑ a ˉ ij x j = b ˉ i ′ ( b ˉ i − a ˉ i e a ˉ l e b ˉ l ) − 系数对 x B l ( − a ˉ i e a ˉ l e 1 ) x B l − j = e ∑ a ˉ ij ′ ( a ˉ ij − a ˉ i e a ˉ l e a ˉ l j ) x j
在新字典里,
x B l x_{B_l} x B l 已经变成非基变量(因为它离开了基),所以它会出现在右侧。这没问题:新非基集合是
N ′ = ( N ∖ e ) ∪ B l N'=(N\setminus{e})\cup{B_l} N ′ = ( N ∖ e ) ∪ B l 。
因此更新公式:
RHS:
b ˉ i ′ = b ˉ i − a ˉ i e b ˉ l a ˉ l e ( i ≠ l ) \boxed{\ \bar b'_i=\bar b_i-\bar a_{ie}\frac{\bar b_l}{\bar a_{le}}\ }\quad (i\neq l) b ˉ i ′ = b ˉ i − a ˉ i e a ˉ l e b ˉ l ( i = l )
非进基列 j ≠ e j\neq e j = e :
a ˉ i j ′ = a ˉ i j − a ˉ i e a ˉ l j a ˉ l e ( i ≠ l , j ≠ e ) \boxed{\ \bar a'_{ij}=\bar a_{ij}-\bar a_{ie}\frac{\bar a_{lj}}{\bar a_{le}}\ }\quad (i\neq l,\ j\neq e) a ˉ ij ′ = a ˉ ij − a ˉ i e a ˉ l e a ˉ l j ( i = l , j = e )
对新加入的非基变量 x B l x_{B_l} x B l 的系数(如果你显式保留这列):
a ˉ i , B l ′ = − a ˉ i e 1 a ˉ l e ( i ≠ l ) \boxed{\ \bar a'_{i,B_l}= -\bar a_{ie}\frac{1}{\bar a_{le}}\ }\quad (i\neq l) a ˉ i , B l ′ = − a ˉ i e a ˉ l e 1 ( i = l )
3.2 更新 pivot 行(新的基变量是 x e x_e x e )
由
P 0 P0 P 0 直接得到新基行(把它写成“基变量 = 常数 − …”):
x e = b ˉ l a ˉ l e ⏟ b ˉ e ′ − 1 a ˉ l e x B l − ∑ j ≠ e a ˉ l j a ˉ l e x j \boxed{
x_e=\underbrace{\frac{\bar b_l}{\bar a_{le}}}_{\bar b'_e}
-\frac{1}{\bar a_{le}}x_{B_l}
-\sum_{j\neq e}\frac{\bar a_{lj}}{\bar a_{le}}x_j
} x e = b ˉ e ′ a ˉ l e b ˉ l − a ˉ l e 1 x B l − j = e ∑ a ˉ l e a ˉ l j x j
即 pivot 行更新:
RHS:b ˉ e ′ = b ˉ l / a ˉ l e \bar b'_e=\bar b_l/\bar a_{le} b ˉ e ′ = b ˉ l / a ˉ l e 。
对 ( j ≠ e ) (j\neq e) ( j = e ) :a ˉ e j ′ = a ˉ l j / a ˉ l e \bar a'_{e j}= \bar a_{l j}/\bar a_{le} a ˉ e j ′ = a ˉ l j / a ˉ l e (注意在字典写法里右侧是 “(− a ˉ e j ′ x j -\bar a'_{e j}x_j − a ˉ e j ′ x j )”,所以很多实现里会存带符号的系数;你只要一致就行)。
对离基变量 ( B l ) (B_l) ( B l ) :系数是 ( 1 / a ˉ l e ) (1/\bar a_{le}) ( 1/ a ˉ l e ) (同样注意符号约定)。
3.3 更新目标行
原来目标:
z = z 0 + c ˉ e x e + ∑ j ≠ e c ˉ j x j z=z_0+\bar c_e x_e+\sum_{j\neq e}\bar c_j x_j z = z 0 + c ˉ e x e + j = e ∑ c ˉ j x j
z = z 0 + c ˉ e ( b ˉ l a ˉ l e − 1 a ˉ l e x B l − ∑ j ≠ e a ˉ l j a ˉ l e x j ) + ∑ j ≠ e c ˉ j x j = ( z 0 + c ˉ e b ˉ l a ˉ l e ) ⏟ z 0 ′ + ( − c ˉ e 1 a ˉ l e ) ⏟ 对 x B l 的新检验数 x B l + ∑ j ≠ e ( c ˉ j − c ˉ e a ˉ l j a ˉ l e ) ⏟ c ˉ j ′ x j \begin{aligned}
z
&=z_0+\bar c_e\Big(\frac{\bar b_l}{\bar a_{le}}-\frac{1}{\bar a_{le}}x_{B_l}-\sum_{j\neq e}\frac{\bar a_{lj}}{\bar a_{le}}x_j\Big)+\sum_{j\neq e}\bar c_j x_j\\
&=\underbrace{\Big(z_0+\bar c_e\frac{\bar b_l}{\bar a_{le}}\Big)}_{z_0'}
+\underbrace{\Big(-\bar c_e\frac{1}{\bar a_{le}}\Big)}_{\text{对 }x_{B_l}\text{ 的新检验数}}x_{B_l}
+\sum_{j\neq e}\underbrace{\Big(\bar c_j-\bar c_e\frac{\bar a_{lj}}{\bar a_{le}}\Big)}_{\bar c'_j}x_j
\end{aligned} z = z 0 + c ˉ e ( a ˉ l e b ˉ l − a ˉ l e 1 x B l − j = e ∑ a ˉ l e a ˉ l j x j ) + j = e ∑ c ˉ j x j = z 0 ′ ( z 0 + c ˉ e a ˉ l e b ˉ l ) + 对 x B l 的新检验数 ( − c ˉ e a ˉ l e 1 ) x B l + j = e ∑ c ˉ j ′ ( c ˉ j − c ˉ e a ˉ l e a ˉ l j ) x j
所以:
新目标常数项:
z 0 ′ = z 0 + c ˉ e b ˉ l a ˉ l e \boxed{\ z_0' = z_0 + \bar c_e\frac{\bar b_l}{\bar a_{le}}\ } z 0 ′ = z 0 + c ˉ e a ˉ l e b ˉ l
新检验数(对原非基 j ≠ e j\neq e j = e ):
c ˉ j ′ = c ˉ j − c ˉ e a ˉ l j a ˉ l e ( j ≠ e ) \boxed{\ \bar c'_j = \bar c_j-\bar c_e\frac{\bar a_{lj}}{\bar a_{le}}\ }\quad (j\neq e) c ˉ j ′ = c ˉ j − c ˉ e a ˉ l e a ˉ l j ( j = e )
对新非基变量 x B l x_{B_l} x B l 的检验数:
c ˉ B l ′ = − c ˉ e 1 a ˉ l e \boxed{\ \bar c'_{B_l} = -\bar c_e\frac{1}{\bar a_{le}}\ } c ˉ B l ′ = − c ˉ e a ˉ l e 1
总结做法
单纯形表其实就是把字典的系数整理成一个表,并把“更新字典”实现成一次 pivot :
选 pivot 元素:出基行 l l l 、进基列 e e e ,pivot 值 p = a ˉ l e p=\bar a_{le} p = a ˉ l e 。
pivot 行更新:整行除以 p p p ,让 pivot 变成 1。
消元:对每一行 ( r ≠ l ) (r\neq l) ( r = l ) (包括目标行),做
row r ← row r − ( row r [ e ] ) ⋅ row l \text{row}_r \leftarrow \text{row}_r - (\text{row}_r[e])\cdot \text{row}_l row r ← row r − ( row r [ e ]) ⋅ row l
P13337【模板】线性规划
要输出方案数,直接将非基变量输出就行,问题在于不保证
b i ≥ 0 b_i \ge 0 b i ≥ 0 ,所以需要两次单纯形,第一次单纯形求解一组可行解,第二次单纯形求解最大目标值,这里讲解第一次单纯形的方法。
1. 保证 b i b_i b i 非负
如果
b i ≤ 0 b_i \le 0 b i ≤ 0 ,就将整行乘以
− 1 -1 − 1 ,使得 RHS 非负。
2. 引入松弛和剩余变量:
≤ \le ≤ :加松弛变量
s i ≥ 0 s_i\ge 0 s i ≥ 0 。
a i x + s i = b i a_i x + s_i = b_i a i x + s i = b i
≥ \ge ≥ :减去剩余变量
s i ≥ 0 s_i\ge 0 s i ≥ 0 。
a i x − s i = b i a_i x - s_i = b_i a i x − s i = b i
这里的系数是
− 1 -1 − 1 ,
不能 直接用
s i s_i s i 当基变量(基需要像单位列那样的
+ 1 +1 + 1 )。
(=) :不加松弛也不加剩余,直接
a i x = b i a_i x = b_i a i x = b i
也没有现成的“单位列”可入基。
3. 引入人工变量:
所以对 2、3 两类,要引入
人工变量 a i ≥ 0 a_i\ge 0 a i ≥ 0 :
≥ \ge ≥ :a i x − s i + a i = b i a_i x - s_i + a_i = b_i a i x − s i + a i = b i 。
= = = :a i x + a i = b i a_i x + a_i = b_i a i x + a i = b i 。
4. 构造辅助目标函数
不管你原来要 max 还是 min,它的目标是把所有人工变量都压到 0 。
常见写法:
min w = ∑ a i \min\ w=\sum a_i min w = ∑ a i
如果最优值
w > 0 w>0 w > 0 :说明
无论怎么选 x x x ,都必须让至少一个人工变量 > 0 > 0 > 0 才能满足等式系统 ⇒ 原问题
不可行 。
5. 跑单纯形
初始基变量就是人工变量,初始 RHS 就是
b i b_i b i 。
6. 进入第 2 次单纯形
把人工变量列删掉。
处理“人工变量仍在基里”的情况。
即便 w = 0 w=0 w = 0 ,有时会出现某个人工变量仍是基变量,但它的值是 0。这时必须把它“换出基”,否则你删列会把基搞坏。
做法:
在该行里找一个非人工变量 列,且该列系数不为 0,把它 pivot 进来,把人工变量 pivot 出去。
如果这一行里除人工变量外所有系数都为 0,而且 RHS 也为 0,说明这条约束线性相关,可以把这行删除。
P3980 志愿者招募
题意:
这个项目需要
n n n 天才能完成,其中第
i i i 天至少需要
a i a_i a i 个人。布布通过了解得知,一共有
m m m 类志愿者可以招募。其中第
i i i 类可以从第
s i s_i s i 天工作到第
t i t_i t i 天,招募费用是每人
c i c_i c i 元,求最小花费。
n ≤ 1000 , m ≤ 10000 n \le 1000,m\le 10000 n ≤ 1000 , m ≤ 10000 。
题解:
设
x i x_i x i 表示第
i i i 个志愿者是否选择,
b i j b_{ij} b ij 表示第
i i i 个志愿者在第
j j j 天是否工作,那么问题就是:
minimize ∑ i = 1 m c i x i \begin{aligned} \text{minimize} \quad & \sum_{i=1}^m c_i x_i \\
\end{aligned} minimize i = 1 ∑ m c i x i
subject to ∑ i = 1 m b j i x i ≥ a j ( j = 1 , 2 , … , n ) x i ≥ 0 \begin{aligned}\text{subject to} \quad & \sum_{i=1}^m b_{ji} x_i \ge a_j \quad (j=1,2,\dots,n) \\ & x_i \ge 0 \end{aligned} subject to i = 1 ∑ m b ji x i ≥ a j ( j = 1 , 2 , … , n ) x i ≥ 0
因为单纯形法是求解最大化目标的问题,给问题对偶一下:
maximize ∑ j = 1 n a j y j \begin{aligned} \text{maximize} \quad & \sum_{j=1}^n a_j y_j \\
\end{aligned} maximize j = 1 ∑ n a j y j
subject to ∑ i = 1 n b i j y i ≤ c j ( j = 1 , 2 , … , m ) y i ≥ 0 \begin{aligned}\text{subject to} \quad & \sum_{i=1}^n b_{ij} y_i \le c_j \quad (j=1,2,\dots,m) \\ & y_i \ge 0 \end{aligned} subject to i = 1 ∑ n b ij y i ≤ c j ( j = 1 , 2 , … , m ) y i ≥ 0
因为每个志愿者可以工作的区间是连续的,所以 b 每一行的 1 构成一个区间,转置一下满足引理的判定条件,所以 b 是全幺模矩阵,可以将问题转化为线性规划问题,直接用单纯形法求解即可。
P3337 防守战线
题意:
战线可以看作一个长度为
n n n 的序列,现在需要在这个序列上建塔来防守敌兵,在序列第
i i i 号位置上建一座塔有
C i C_i C i 的花费,且一个位置可以建任意多的塔,费用累加计算。有
m m m 个区间
[ L 1 , R 1 ] , [ L 2 , R 2 ] , ⋯ , [ L m , R m ] [L_1, R_1], [L_2, R_2], \cdots, [L_m, R_m] [ L 1 , R 1 ] , [ L 2 , R 2 ] , ⋯ , [ L m , R m ] ,在第
i i i 个区间的范围内要建至少
D i D_i D i 座塔。求最少花费。
n ≤ 1000 , m ≤ 10000 n \le 1000,m \le 10000 n ≤ 1000 , m ≤ 10000 。
题解:
设
x i x_i x i 表示序列第
i i i 个位置选了几个塔,
b i j b_{ij} b ij 表示对于第
i i i 个区间第
j j j 个位置是否在区间里面,那么问题就是:
minimize ∑ i = 1 n c i x i \begin{aligned} \text{minimize} \quad & \sum_{i=1}^n c_i x_i \\
\end{aligned} minimize i = 1 ∑ n c i x i
subject to ∑ i = 1 n b j i x i ≥ d j ( j = 1 , 2 , … , m ) x i ≥ 0 \begin{aligned}\text{subject to} \quad & \sum_{i=1}^n b_{ji} x_i \ge d_j \quad (j=1,2,\dots,m) \\ & x_i \ge 0 \end{aligned} subject to i = 1 ∑ n b ji x i ≥ d j ( j = 1 , 2 , … , m ) x i ≥ 0
因为单纯形法是求解最大化目标的问题,给问题对偶一下:
maximize ∑ j = 1 m d j y j \begin{aligned} \text{maximize} \quad & \sum_{j=1}^m d_j y_j \\
\end{aligned} maximize j = 1 ∑ m d j y j
subject to ∑ i = 1 m b i j y i ≤ c j ( j = 1 , 2 , … , n ) y i ≥ 0 \begin{aligned}\text{subject to} \quad & \sum_{i=1}^m b_{ij} y_i \le c_j \quad (j=1,2,\dots,n) \\ & y_i \ge 0 \end{aligned} subject to i = 1 ∑ m b ij y i ≤ c j ( j = 1 , 2 , … , n ) y i ≥ 0
因为区间的位置是连续的,同上是全幺模矩阵,所以可以直接单纯形。
SGU 278 Fuel
题意:
一个燃料站有无限量的
N N N 种燃料。每种燃料的密度为
a i a_i a i ,成本为
b i b_i b i ,强度为
c i c_i c i 。
m m m 公斤这种燃料的体积为
m × a i m \times a_i m × a i ,强度为
m × c i m \times c_i m × c i ,成本为
m × b i m \times b_i m × b i 美元。您的汽车可以储存各种燃料的混合物,但总容量不得超过
A A A 。你有
B B B 美元。你的任务是确定你能购买的最大燃料体积。请注意,你可以购买任何非负数的任何种类的燃料,而不一定是整数。(注意到这个体积计算公式不满足物理公式)
提示:这题考虑单老哥。
题解:
max ∑ i = 1 N c i m i \max \sum_{i=1}^N c_i m_i max i = 1 ∑ N c i m i
s.t. ∑ a i m i ≤ A , ∑ b i m i ≤ B , m i ≥ 0 \text{s.t.}\quad \sum a_i m_i \le A,\quad \sum b_i m_i \le B,\quad m_i\ge 0 s.t. ∑ a i m i ≤ A , ∑ b i m i ≤ B , m i ≥ 0
发现这个线性规划只有两个约束,因为最优解一定会落在顶点上,所以最优解最多用到两种燃料,但是不能直接枚举
O ( n 2 ) O(n^2) O ( n 2 ) 会超时。
令
x i = c i m i ( 第 i 种燃料贡献的强度 ) ⇒ m i = x i c i x_i = c_i m_i \quad (\text{第 }i\text{ 种燃料贡献的强度})
\Rightarrow m_i = \frac{x_i}{c_i} x i = c i m i ( 第 i 种燃料贡献的强度 ) ⇒ m i = c i x i
代回约束:
∑ a i c i x i ≤ A , ∑ b i c i x i ≤ B , x i ≥ 0 \sum \frac{a_i}{c_i} x_i \le A,\qquad
\sum \frac{b_i}{c_i} x_i \le B,\qquad
x_i\ge 0 ∑ c i a i x i ≤ A , ∑ c i b i x i ≤ B , x i ≥ 0
目标变成
max ∑ x i \max \sum x_i max ∑ x i
解释非常直观:
想获得 1 单位强度 ,用燃料 i i i 要消耗
p i = a i c i ( 体积/强度 ) , q i = b i c i ( 金钱/强度 ) p_i=\frac{a_i}{c_i}\ (\text{体积/强度}),\quad q_i=\frac{b_i}{c_i}\ (\text{金钱/强度}) p i = c i a i ( 体积 / 强度 ) , q i = c i b i ( 金钱 / 强度 )
所以每种燃料对应平面点 P i = ( p i , q i ) P_i=(p_i,q_i) P i = ( p i , q i ) 。
如果总强度
S = ∑ x i S=\sum x_i S = ∑ x i ,则总消耗为
( 体积 , 金钱 ) = ( ∑ p i x i , ∑ q i x i ) = S ⋅ ( ∑ λ i p i , ∑ λ i q i ) (\text{体积},\text{金钱})=
\left(\sum p_i x_i,\ \sum q_i x_i\right)
= S\cdot \left(\sum \lambda_i p_i,\ \sum \lambda_i q_i\right) ( 体积 , 金钱 ) = ( ∑ p i x i , ∑ q i x i ) = S ⋅ ( ∑ λ i p i , ∑ λ i q i )
其中
λ i = x i / S \lambda_i=x_i/S λ i = x i / S ,满足
λ i ≥ 0 , ∑ λ i = 1 \lambda_i\ge 0,\ \sum\lambda_i=1 λ i ≥ 0 , ∑ λ i = 1 。
也就是说,“单位强度的平均消耗点”
P ˉ = ( p ˉ , q ˉ ) = ∑ λ i P i \bar P=(\bar p,\bar q)=\sum \lambda_i P_i P ˉ = ( p ˉ , q ˉ ) = ∑ λ i P i
可以是
所有点 P i P_i P i 的凸包中的任意点 。
而可行性要求
S p ˉ ≤ A , S q ˉ ≤ B ⇒ S ≤ min ( A p ˉ , B q ˉ ) S\bar p\le A,\quad S\bar q\le B
\Rightarrow
S\le \min\left(\frac{A}{\bar p},\frac{B}{\bar q}\right) S p ˉ ≤ A , S q ˉ ≤ B ⇒ S ≤ min ( p ˉ A , q ˉ B )
因此答案变为:
在凸包
conv ( P i ) \text{conv}({P_i}) conv ( P i ) 上,最大化
F ( p , q ) = min ( A p , B q ) F(p,q)=\min\left(\frac{A}{p},\frac{B}{q}\right) F ( p , q ) = min ( p A , q B )
若存在
P j = ( p j , q j ) P_j=(p_j,q_j) P j = ( p j , q j ) 满足
p j ≤ p i , q j ≤ q i p_j\le p_i,\ q_j\le q_i p j ≤ p i , q j ≤ q i
则燃料
i i i 每 1 强度消耗不优于
j j j ,永远没必要用
i i i 。
所以先按
p p p 升序,保留
q q q 严格下降的一条链即可。
设
k = B A k=\frac{B}{A} k = A B
若 q ≤ k p q\le kp q ≤ k p ,则 A p ≤ B q \frac{A}{p}\le \frac{B}{q} p A ≤ q B ,体积约束更紧,F ( p , q ) = A p F(p,q)=\frac{A}{p} F ( p , q ) = p A 。
若 q ≥ k p q\ge kp q ≥ k p ,则钱更紧,F ( p , q ) = B q F(p,q)=\frac{B}{q} F ( p , q ) = q B 。
因此最优点要么:
落在某个凸包顶点(只卡住一个约束时),
落在凸包边与直线 q = k p q=kp q = k p 的交点(两个约束同时卡住:A p = B q \frac{A}{p}=\frac{B}{q} p A = q B ,此时一定最优)。
做法:
先对所有凸包顶点算 F F F 更新答案;
再扫描每条边,判断端点 ( f = q − k p ) (f=q-kp) ( f = q − k p ) 是否异号,若相交就线段-直线求交点并更新。
复杂度:
O ( N log N ) O(N\log N) O ( N log N ) 。
结语
比较遗憾的是写的东西在线性规划里面偏简单,并没有将线性规划和其他算法联系起来(比如网络流)等。
因时间关系,很多东西的证明被一笔带过或没有,在此将本文并未证明的东西列出来:
弱对偶定理和强对偶定理。
全幺模矩阵推出整数规划问题等价于线性规划问题,这个可以参见:https://www.luogu.com.cn/paste/xi6i8dkm。
如果矩阵的每一列的正负 1 形成一个区间那么就是全幺模。
单纯形的期望运行时间(参见平滑分析)。
二分图匹配和网络流的 LP 可行域是 IP。
原始-对偶方法里面问题 Q4 的可行性。
后续填坑。