线性规划总结

和网络流有密切联系。网络流是线性规划的特殊形式。

一些概念

定义关于变量 \(x_1,x_2,\cdots,x_n\) 的线性函数 \(f(x_1,x_2,\cdots,x_n)=\sum\limits_{i=1}^na_ix_i\)。关于 \(f\) 的等式和不等式称为线性约束。线性规划就是在一组线性约束下最优化目标线性函数值的方法。

标准型

\[\begin{aligned} &\max z=\sum\limits_{i=1}^nc_ix_i\\ &s.t. \begin{cases} \sum\limits_{j=1}^na_{ij}x_j\le b_i & i=1,2,\cdots,m\\ x_j \ge 0 & j=1,2,\cdots n \end{cases} \end{aligned} \]

写成线性代数形式,设 \(\mathrm x\)\(x_i\) 组成的列向量,\(\mathrm c\)\(c_i\) 组成的列向量,\(\mathrm b\)\(b_i\) 组成的列向量,\(A=(a_{ij})_{m\times n}\)

\[\begin{aligned} &\max z=\mathrm c^{T}\mathrm x\\ &s.t. \begin{cases} A\mathrm x\le \mathrm b \\ \mathrm x \ge 0 \end{cases} \end{aligned} \]

(写得不太规范,差不多能懂就行。线代形式是为了后面方便对偶的。)

松弛型

添加辅助变量,将 \(\le\) 改为 \(=\)

\[\begin{aligned} &\max z=\sum\limits_{i=1}^nc_ix_i\\ &s.t. \begin{cases} x_{i+n}= b_i-\sum\limits_{j=1}^na_{ij}x_j & i=1,2,\cdots,m\\ x_j \ge 0 & j=1,2,\cdots n+m \end{cases} \end{aligned} \]

目标最小值,可以乘 \(-1\) 转最大值。约束中是 \(\ge\),也可以乘 \(-1\)\(\le\)。如果一个变量范围没有限制,那么可以拆 \(x=x'-x''\),使得 \(x',x''\ge 0\)

约束中只能是 \(\le\),如果有 \(<\) 是做不了的。

几何意义

有几个可以证明的命题:

  • 可行域是凸的。

  • 最优解在顶点上。

单纯形法就是搜索每个顶点并且剪枝,最终得到最优解。一次 pivot 操作就是移动到下一个顶点。

单纯形法

分三步:

  1. 对于松弛型,找到一个基本可行解。

  2. 进行旋转(pivot)操作来得到更优的解。

  3. 不断重复 2.

找到基本可行解

观察松弛型,

\[\begin{aligned} &\max z=\sum\limits_{i=1}^nc_ix_i\\ &s.t. \begin{cases} x_{i+n}= b_i-\sum\limits_{j=1}^na_{ij}x_j & i=1,2,\cdots,m\\ x_j \ge 0 & j=1,2,\cdots n+m \end{cases} \end{aligned} \]

称第一种约束左边的变量为基本变量,右边的为非基本变量。

如果所有 \(b_i\ge 0\),那么显然所有非基本变量取 \(0\),基本变量取 \(b_i\) 就是一组可行解。

如果存在 \(b_i<0\) 呢?上面的方法不行。有两种方法:创建一个辅助线性规划,或者 OI 中更常用的随机选择法

随机找到一个 \(b_i<0\)。如果没有,则已经有基本可行解。

在这第 \(i\) 条限制中,我们随机找到一个 \(j\) 使得 \(a_{ij}<0\)。将这个 \(x_j\) 换成基本变量(类似移项)即可使 \(b_i'>0\)。如果找不到,说明没有基本可行解。这里将非基本变量换成基本变量,将基本变量换成非基本变量的操作就是 pivot 操作。

代码
void init(){
	while(true){
		int e=0,l=0;
		for(int i=1;i<=m;++i) if(a[i][0]<-eps&&((!l)||(rand()&1))) l=i;
		if(!l) break;
		for(int j=1;j<=n;++j) if(a[l][j]<-eps&&((!e)||(rand()&1))) e=j;
		if(!e){
			cout<<"Infeasible"<<'\n';exit(0);
		}
		pivot(l,e);
	}
}

pivot

类似高消。这里我们简化了矩阵。矩阵中只存了非基本变量前面的系数。因为基本变量前面的系数非 \(0\)\(1\)。为了方便,我们把 \(b\) 塞到第 \(0\) 列。

每次交换前,先要将选中的这一行除以选择的非基本变量前的系数。可以直接存下来这个系数,然后把基本变量的系数交换过来(即 \(1\))。

这一行处理好后,再去更新其他行,类似高消。用其他行 \(i\) 减去选中的这一行 \(j\)。已经交换了基本变量,那一位上的系数应当变成 \(0\)。而又因为原来的非基本变量 \(e\) 的系数是 \(1\),所以其他行 \(i\) 就应当减去对应变量前的系数 \(a_{ie}\) 倍的第 \(j\) 行。

而目标函数是由非基本变量描述的,这里同时更新一下系数。不妨把这个塞在矩阵的第 \(0\) 行,常数项塞在第 \(0\) 列。最终的答案就是矩阵中 \(-a_{00}\)。因为这实际上描述的是 \(-z=a_{00}-\sum\limits_{i=1}^nc_ix_i\)

代码
void pivot(int l,int e){
	swap(id[l+n],id[e]);//id 存第 i 个基本变量的原本编号,用于输出方案,最终方案中每个基本变量取 a[i][0],非基本变量取 0
	ld tmp=a[l][e];a[l][e]=1;
	for(int j=0;j<=n;++j) a[l][j]/=tmp;
	for(int i=0;i<=m;++i){
		if(i==l||fabs(a[i][e])<eps) continue;
		tmp=a[i][e];a[i][e]=0;
		for(int j=0;j<=n;++j) a[i][j]-=tmp*a[l][j];
	}
}

如何 pivot 来更新答案?

\[z=\sum\limits_{i=1}^nc_ix_i \]

\(z\) 到最优解时,\(c_i\) 必然都小于等于 \(0\),否则可以增大对应的变量使 \(z\) 更大。这样就可以判最优解。

当还不是最优解时,首先选一个 \(c_e>0\),即选择非基本变量 \(e\)。这表示我们可以增大 \(x_e\) 的值。但是还要保证增大后满足各个约束,一个约束中,\(x_e\) 的最大值为 \(\dfrac{a_{i0}}{a_{ie}}\)。所有约束中应当取最小值。找到这一行,记作 \(l\)。然后就将 \(e\) 与这一行的基本变量交换,即 pivot。

形式化一点。首先找到一个 \(e\) 使得 \(a_{0e}>0\)。如果没有,说明已经是最优解,退出。

再找到一个 \(l\) 使得 \(a_{le}>0\)\(\dfrac{a_{l0}}{a_{le}}\) 最小。如果没有,说明 \(x_e\) 可以无穷大,答案无界。

否则找到了 \(l,e\),进行 pivot。

防止进入循环,遵循 Bland 规则。

  • \(l\) 选约束最紧的。

  • \(e\) 选下标最小的。

代码
void simplex(){
	while(true){
		int e=0,l=0;ld mi=inf;
		for(int i=1;i<=n;++i){
			if(a[0][i]>eps){
				e=i;break;
			}
		}
		if(!e) break;
		for(int i=1;i<=m;++i){
			if(a[i][e]>eps&&mi>a[i][0]/a[i][e]){
				mi=a[i][0]/a[i][e];l=i;
			}
		}
		if(!l){
			cout<<"Unbounded"<<'\n';exit(0);
		}
		pivot(l,e);
	}
}

时间复杂度分析

一次 pivot 是 \(O(nm)\) 的,分析 pivot 的次数,最坏可达指数级(遍历可行域的每个顶点)。可以在一开始进行若干次随机扰动达到期望线性。是有严格多项式复杂度的算法,但没必要学。单纯形法大部分时候表现良好。

代码
void noise(){
	for(int i=1;i<=10;++i){
		int l=0,e=0;
		for(int i=1;i<=m;++i) if(fabs(a[i][0])>eps&&((!l)||(rand()&1))) l=i;
		if(!l) break;
		for(int i=1;i<=n;++i) if(fabs(a[l][i])>eps&&((!e)||(rand()&1))) e=i;
		if(!e) continue;
		pivot(l,e);
	}
}

对偶定理

对于原问题:

\[\begin{aligned} &\min z=\mathrm c^{T}\mathrm x\\ &s.t. \begin{cases} A\mathrm x\ge \mathrm b \\ \mathrm x \ge 0 \end{cases} \end{aligned} \]

可以对偶为:

\[\begin{aligned} &\max w=\mathrm b^{T}\mathrm y\\ &s.t. \begin{cases} A^T\mathrm y\le \mathrm c \\ \mathrm y \ge 0 \end{cases} \end{aligned} \]

就是 \(\min,\max\) 互换,\(A\) 转置,\(\mathrm b,\mathrm c\) 互换,\(\mathrm x\) 改成 \(\mathrm y\),变量 \(\ge 0\) 的约束不变,其他约束 \(\le,\ge\) 互换。

可以证明线性规划的原问题的最优解等于对偶问题的最优解。

不加证明地给出一些结论:

  • 互为对偶的两个问题,或者同时有最优解,或者同时都没有最优解。

  • 对偶有可行解,原问题不一定有可行解。因为对偶问题的可行解可能是无界解,原问题可能无可行解。

  • 原问题有无穷多最优解,只能说明对偶有最优解,不能说明组数。

  • 一对问题,若一个有可行解一个无可行解,则有可行解的是无界解。

  • 原问题有无界解,对偶问题无可行解。

全幺模矩阵

当一个矩阵的任意一个子方阵的行列式为 \(\pm 1\)\(0\) 时,称之全幺模。

如果单纯形法的矩阵是全幺模的,那么线性规划有整数解。

任何最大流,最小费用最大流的单纯形矩阵都是全幺模的。

posted @ 2025-06-03 16:29  RandomShuffle  阅读(58)  评论(0)    收藏  举报