线性规划
基础线性规划
一.定义
线性函数:\(f(x_1,x_2, \dots, x_n) = \sum\limits_{i = 1}^{n}c_ix_i\)。更简洁的写法是将 \(x_i, c_i\) 看作 \(\mathbb{R}^n\) 的一组行向量,此时 \(f(x_1, x_2, \dots, x_n) = c^Tx\)。
线性约束:等式 \(f(x_1, x_2, \dots, x_n) = b\) 和不等式 \(f(x_1, x_2, \dots, x_n) \leq b, f(x_1, x_2, \dots, x_n) \geq b\) 称为线性约束。
线性规划:满足若干个线性约束时,求某个线性函数最值的规划问题。
定义 \(a \leq b \Rightarrow \forall i \in [1, n], a_i \leq b_i\),\(a\geq b \Rightarrow \forall i \in [1,n], a_i \geq b_i\)。
二.标准型
一个有 \(n\) 个自变量,\(m\) 个限制的线性规划标准型形如:
满足约束:
简写为:
对于非标准型,可以通过以下方式转化为标准型:
- 取 \(\min\),所有 \(c_i\) 取负。
- 约束 $\geq $,所有 \(a_{i, k}, b_i\) 取负。
- 线性等式转化为 $\geq $ 和 $\leq $。
- 变量无限制的,转化为两个 \(\geq 0\) 变量作差。
三.松弛型
松弛型线性规划将所有约束转化到单个变量,每个约束新加变量 \(x_{n+i}\),形如:
其中,\(x_1, \dots, x_n\) 称为非基变量,\(x_{n+1}, \dots, x_{n+m}\) 为基变量。
四.线性规划的求解:单纯形法
在 \(b\ge 0\) 的情况下,只需要令非基变量都为 \(0\),就可以得到一组解。称为基本解。
单纯形法通过不断调整基变量和非基变量,将问题转化为另一个等价的松弛型的线性规划,从而得到更优的解。
基本算法流程
-
\(\operatorname{pivot}\) 操作
我们需要不断地交换基变量和非基变量使得答案变大。
\(\operatorname{pivot}(l,e)\) 将基变量 \(l\) 与非基变量 \(e\) 进行交换。
首先,通过对 \(x_l\) 那一行的式子通过移项,使得等式左边为 \(x_e\)。再将这个式子带入剩下式子中的 \(x_e\)。
-
主算法
- 在非基变量中找到一个 \(c_e>0\) 的 \(e\)。如果没有就返回 \(v\)。
- 在基变量中找到一个 \(l\),使得 \(a_{le}>0\) 且 \(\frac{b_l}{a_{le}}\) 最小(限制最紧)。如果没找到则无解(最值为 \(\infty\))。
- 执行 \(\operatorname{pivot}(l,e)\)。然后返回第一步。
停止性
可以发现,在整个流程中 \(v\) 是不降的。\(v\) 上升的情况好说,那 \(v\) 不变时,是否操作会形成一个环,从而算法死循环呢?
可以证明,只要我们选的 \(e\) 和 \(l\) 是字典序最小的,就不会出现循环的情况(Bland 规则)。
一份简易实现(uoj 上 t 成 97):
namespace Solver {
int n, m, t;
db v[M][M]; int id[2 * M];
inline void pivot(int r, int c) {
std :: swap(id[r + n], id[c]); db coe = 1. / (-v[r][c]); v[r][c] = -1.;
for(int i = 0; i <= n; ++i) v[r][i] *= coe;
for(int i = 0; i <= m; ++i) if(r ^ i) {
coe = v[i][c]; v[i][c] = 0;
for(int j = 0; j <= n; ++j) v[i][j] += coe * v[r][j];
}
}
inline void init_b() {
while(1) {
int fre = 0, bas = 0;
for(int i = 1; i <= m; ++i) if(v[i][0] < -eps && (!bas || (rand() & 1))) bas = i;
if(!bas) return ;
for(int i = 1; i <= n; ++i) if(v[bas][i] > eps && (!fre || (rand() & 1))) fre = i;
if(!fre) return puts("Infeasible"), exit(0), void();
pivot(bas, fre);
}
}
inline void simplex() {
std :: iota(id + 1, id + n + m + 1, 1); init_b();
while(1) {
int fre = 0, bas = 0;
for(int i = 1; i <= n; ++i) if(v[0][i] > eps && (!fre || id[i] < id[fre])) fre = i;
if(!fre) break ; db mn = 1e30;
for(int i = 1; i <= m; ++i) if(v[i][fre] < -eps && v[i][0] / (-v[i][fre]) < mn) bas = i, mn = v[i][0] / (-v[i][fre]);
if(!bas) return puts("Unbounded"), exit(0), void();
pivot(bas, fre);
}
}
int to[M * 2];
inline void mian() {
srand((unsigned)time(NULL));
n = read(), m = read(), t = read();
for(int i = 1; i <= n; ++i) v[0][i] = read();
for(int i = 1; i <= m; ++i) {
for(int j = 1; j <= n; ++j) v[i][j] = -read(); v[i][0] = read();
}
simplex(); printf("%.8Lf\n", (long double)v[0][0]); if(!t) return;
for(int i = 1; i <= n + m; ++i) to[id[i]] = i;
for(int i = 1; i <= n; ++i) printf("%.8Lf ", (to[i] <= n) ? 0 : (long double)v[to[i] - n][0]);
}
}
虽然单纯型法的时间复杂度是指数级的,但在实战中相当优秀。
加入理论复杂度更低的算法有椭球算法和内点算法,达到了多项式复杂度。但在 OI 中并不比单纯形法好到哪里去,并不常用。
一些特殊的线性规划可以通过网络流或者计算几何相关技巧进行求解。
整数线性规划
整数线性规划在原先的限制中加入所有变量都是整数的限制。
整数线性规划是 NP-Hard 问题。
但整数线性规划和一般线性规划有整数解是两个不同的概念。例如大部分网络流可以写成线性规划的形式,只要网络流的流量上限 \(c\) 是整数,就一定存在整数最优解,与网络流本身是实线性规划并不矛盾,且此时整数线性规划和实线性规划得到的解相同。
五.线性规划对偶
考虑如下线性规划:
满足:
由于变量非负,我们显然可以得到 \(7x_1+x_2+5x_3 \geq x_1 - x_2 +x_3 \geq 10\)。
再将第一个限制和第二个限制做一个加权和:\((x_1-x_2+x_3)+(5x_1+2x_2-x_3) = 6x_1+x_2 \geq 16\),故 \(7x_1+x_2+5x_3 \geq 16\)。 因此,我们可以通过任意多个限制的加权和 “凑出”更紧凑的界,能凑出的最大值就是理论上目标函数的最小值。
更一般地,设每个约束的加权为 \(y_i\),则要满足 \(\sum\limits_{k = 1}^{m}a_{k, i}y_k \leq c_i\),要求最大化:\(\sum\limits_{i = 1}^{m}y_ib_i\),这就变成了一个新的线性规划问题,以上面的线性规划为例:
满足:
我们称初始的线性规划为原问题,转化为的线性规划问题为对偶问题。
如果将对偶问题再进行一次对偶操作,则又回到了原问题。一个最大化问题可对偶成一个最小化问题,最小化问题可对偶成一个最大化问题。
线性规划对偶:对于线性规划
定义其对偶为:
线性规划对偶性
线性规划弱对偶性:若 \(x_i\) 是原线性规划的一组可行解,\(y_i\) 是对偶规划的可行解,则有:
证明:根据上面推导可得:\(c_i \geq \sum\limits_{k = 1}^{m}a_{k, i}y_i\),\(\sum\limits_{i = 1}^{n}c_ix_i \geq \sum\limits_{i = 1}^{n}(\sum\limits_{k = 1}^ma_{k, i}y_i)x_i = \sum\limits_{k = 1}^{m}(\sum\limits_{i = 1}^{n}a_{k, i}x_i)y_i \geq \sum\limits_{k = 1}^{m}b_ky_k\)
这只说明了对偶问题可以达到原问题的一个下界,而下面的定理则说明原问题与对偶问题最优解相同。
线性规划强对偶性:若 \(x_i\) 是原线性规划的一组可行解,\(y_i\) 是对偶规划的可行解,则有:
只需要证明此时的 $\geq $ 成立即可,但证明过于繁琐,略去不提。
一些经典问题的对偶
最大流
加入边 \(f_{v, u}\) 后,所有点都可以平衡,设 \(c_{v, u} = \infty\),则有线性规划:
满足约束
最小割
考虑用什么样的方式描述一条边是割边:当且仅当 \((u, v)\),\(u \in S\),\(v \notin S\) 时成立,因此设 \(\varphi_u = [u \in S]\),\(d_{u, v}\) 表示 \((u, v)\) 是否是割边,\(d_{u, v} = \max(0, \varphi_u - \varphi_v)\),即 \(d_{u, v} \geq \max(0, \varphi_u - \varphi_v), -d_{u, v} \leq -\max(0, \varphi_u - \varphi_v)\),若强制令 \(d_{u, v}, \varphi_u \in [0, 1]\),在最小化前提下,简化为 \(d_{u, v} \geq \varphi_u - \varphi_v\),可写出线性规划:
满足约束
我们考虑对最大流的线性规划进行对偶,设 \(f_{u, v} \leq c_{u, v}\) 的对偶变量为 \(\delta_{u, v}\)。\(\sum\limits_{(u, v) \in E}f_{u, v} - \sum\limits_{(v, u) \in E} f_{v, u} = 0\) 的对偶变量为 \(\phi_u\) ,写出对偶规划为:
由于最大流等于最小割,这两个规划等价,但是由于第一个规划中有 \(d_{u, v}, \varphi_u \in [0, 1]\) 的限制。不过可以证明,一定存在一个最优解满足上面的限制,略去不谈。
二分图最大权匹配
设 \(L, R\) 分别为左,右部点,容易写出二分图最大权匹配的线性规划:
满足约束
设左部点约束的对偶变量为 \(p_u\),右部点约束的对偶变量为 \(q_v\),则其对偶为:
满足约束
还记得 KM 算法吗?在 KM 算法中,我们维护的顶标就是 \(p_u, q_v\)。因此,二分图最大权匹配等于最小顶标和。
特别地,当 \(w = 1\) 时,上面的对偶得到最大匹配等于最小点覆盖。
例题 1:2486 -- 【Codechef-FEB2012】飞行距离Flight Distance
\(n\) 个点的无向图 \(G\),可修改任意一条边的权值,代价为修改前后权值差绝对值之和。现在要求修改一些边的权值,使得任意两个相邻点之间最短路等于两个相邻点之间边的权值。
设 \(a_{u, v}\) 表示增加的权值,\(b_{u, v}\) 表示减少的权值,设原边权为 \(w_{i, j}\),对于初始存在的边 \((u, v)\),修改后的权值为 \(d_{i, j} = w_{i, j} +a_{i, j} - b_{i, j}\)。 还要满足任意 \((u, k) \in E\),有:\(d_{u, v} \leq w_{i, k} +a_{i, k} - b_{i, k} +d_{k, v}\),则可以写出线性规划:
满足约束
例题 2:POJ3689 Equations
三个长度为 \(n\) 个数组 \(\{a_i\}, \{b_i \}, \{c_i\}\),多次询问。每次询问给定 \(s, t\),求一组非负实数 \(x_i\) 满足:
\(\sum\limits_{i = 1}^{n}a_ix_i = s\),\(\sum\limits_{i = 1}^{n}b_ix_i = t\)。同时最大化 \(\sum\limits_{i = 1}^{n}c_ix_i\)。
例题 3:【CodeChef June 15】厨书CHEFBOOK