单纯形 Simplex 学习笔记

前言:这些是一些大神的博客
https://www.cnblogs.com/ECJTUACM-873284962/p/7097864.html
https://ldxcaicai.github.io/LP/


线性规划

标准型:
m a x ( i = 1 n c i x i ) max(\sum_{i=1}^n c_ix_i)
l i m i t : j = 1 n a i , j x j b i ,   x 0 limit:\sum_{j=1}^na_{i,j}*x_j\le b_i,\ x\ge 0
松弛型:
m a x ( i = 1 n c i x i ) max(\sum_{i=1}^n c_ix_i)
l i m i t : j = 1 n a i , j x j = b i ,   x 0 limit:\sum_{j=1}^na_{i,j}*x_j= b_i,\ x\ge 0


标准型与松弛型的转换:
x i + m = b i j = 1 n a i , j x j x_{i+m}=b_i-\sum_{j=1}^na_{i,j}*x_j ,那么有 j = 1 n a i , j x j + x i + m = b i \sum_{j=1}^na_{i,j}*x_j+x_{i+m}= b_i

单纯形求解线性规划:

一直重复以下操作:

  1. 在目标函数中找到一个 c e 0 c_e\ge 0 e e
  2. 在约束条件中找到一个 a l , e > 0 a_{l,e}>0 且使 b l a l , e \frac{b_l}{a_{l,e}} 最大的 l l
  3. 将第 l l 个等式进行移项, x e = b [ l ] j ! = e a [ l ] [ j ] x l + m a [ l ] [ e ] x_e=\frac{b[l]-\sum_{j!=e}a[l][j]-x_{l+m}}{a[l][e]}
  4. 带到其他行,消去 x e x_e ,新代入 x l + m x_{l+m}
  5. 当无法找到 e e 时,说明已经最优
  6. 当无法找到合法的 l l 时,说明该线性规划无界

令:当存在 b [ i ] < 0 b[i]<0 的时候可能会出锅,可以选择对 b [ i ] < 0 b[i]<0 的列随机扰动
具体实现参考代码


满足线性规划问题约束条件的所有点组成的集合就是线性规划的可行域。若可行域有界(以下主要考虑有界可行域),线性规划问题的目标函数最优解必然在可行域的顶点上达到最优。

单纯形法就是通过设置不同的基向量,经过矩阵的线性变换,求得基可行解(可行域顶点),并判断该解是否最优,否则继续设置另一组基向量,重复执行以上步骤,直到找到最优解。所以,单纯形法的求解过程是一个循环迭代的过程


对偶原理:

最大化和最小化可以通过正负来转换,而大于小于号则不行

考虑如下的一个线性规划:

m i n ( i = 1 n c i x i ) min(\sum_{i=1}^n c_ix_i)
l i m i t : j = 1 n a i , j x j b i ,   x 0 limit:\sum_{j=1}^na_{i,j}*x_j\ge b_i,\ x\ge 0

利用待定系数法的思想可以知道,该线性规划的任意可行解 { x 1 , x 2 , . . . , x n } \{x_1,x_2,...,x_n\} 一定能表示成如下形式:

i = 1 n c i x i i = 1 m y i ( j = 1 n a i , j x j ) i = 1 m y i b i \sum_{i=1}^nc_ix_i\ge \sum_{i=1}^my_i(\sum_{j=1}^na_{i,j}*x_j)\ge\sum_{i=1}^my_i*b_i
i = 1 m y i a j , i c j , y i 0 \sum_{i=1}^my_i*a_{j,i}\le c_j,y_i\ge 0

于是可以转换为一个新的线性规划
找到 i = 1 m y i b i \sum_{i=1}^my_i*b_i 的上界就相当于找到 i = 1 n c i x i \sum_{i=1}^nc_i*x_i
但是我们还要证明当 i = 1 m y i b i \sum_{i=1}^my_i*b_i 取到最优解即最大值时, i = 1 n c i x i \sum_{i=1}^nc_i*x_i 也取到最优解即最小值

证明:
可以参考
https://blog.csdn.net/itnerd/article/details/83352441

将线性规划表示成矩阵的形式就是要证这么一个东西:
m i n { c T x A x b , x 0 } max { b T y A T y c , y 0 } min\{c^Tx|Ax\ge b,x\ge 0\}\Leftrightarrow \max\{b^Ty|A^Ty\le c,y\ge 0\}

对于第一个线性规划,构造一个函数 f ( y ) = c T x + y T ( b A x ) , y 0 f(y)=c^Tx+y^T(b-Ax),y\ge 0
g ( y ) = m i n { f ( y ) A x b , x 0 } g(y)=min\{f(y)|Ax\ge b,x\ge0\}
g ( y ) c T x \forall g(y)\le c^Tx m a x ( g ( y ) ) = c T x max(g(y))=c^T x
于是有
m i n ( c T x ) = m a x ( g ( y ) ) min(c^Tx)=max(g(y))
= m a x ( y T b + m i n ( ( c T y T A ) x ) ) =max(y^Tb+min((c^T-y^TA)x))
= m a x ( b T y + m i n ( ( c T y T A ) x ) ) =max(b^Ty+min((c^T-y^TA)x))

c T y T A 0 c^T-y^TA\ge 0 ,那么 m i n ( ( c T y T A ) x ) = 0 min((c^T-y^TA)x)=0
反之 m i n ( ( c T y T A ) x ) = min((c^T-y^TA)x)=-\infty
所以
= m a x ( b T y + m i n ( ( c T y T A ) x ) ) = m a x ( b T y ) =max(b^Ty+min((c^T-y^TA)x))=max(b^Ty)
令:翻到一个感性的证明,有点像经济学上买家卖家的博弈
在这里插入图片描述

线性规划互相松弛定理

在这里插入图片描述


这里给出 NOI 2008 志愿者招募的题解及代码

a i , j a_{i,j} 表示第 i i 类志愿者在第 j j 天是否工作, x i x_i 表示第 i i 类志愿者的个数

那么有线性规划

m i n ( i = 1 m x i c i ) min(\sum_{i=1}^mx_i*c_i)
l i m i t : j = 1 n a j , i x j b i ,   x i 0 limit:\sum_{j=1}^na_{j,i}*x_j\ge b_i,\ x_i\ge 0

对偶过后发现
m a x ( i = 1 n y i b i ) max(\sum_{i=1}^ny_i*b_i)
l i m i t : j = 1 m a i , j y j c i limit:\sum_{j=1}^ma_{i,j}*y_j\le c_i
已经是标准形式了

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 1e3 + 5, M = 1e4 + 5;
cs double eps = 1e-8, INF = 1e15;
int n, m; double a[M][N];
void pivot(int l, int e){
	double t = a[l][e]; a[l][e] = 1;
	for(int i = 0; i <= n; i++) a[l][i] /= t;
	for(int i = 0; i <= m; i++) if(l!=i && fabs(a[i][e]) > eps){
		t = a[i][e]; a[i][e] = 0;
		for(int j = 0; j <= n; j++) a[i][j] -= a[l][j] * t;
	}
}
bool simplex(){
	while(true){
		int l = 0, e = 0; double 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 && a[i][0] / a[i][e] < mi) mi = a[i][0] / a[i][e], l = i;
		if(!l) return false; pivot(l, e);
	} return true;
}
int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; i++) a[0][i] = read();
	for(int i = 1; i <= m; i++){
		int l = read(), r = read(), c = read();
		for(int j = l; j <= r; j++) a[i][j] = 1;
		a[i][0] = c;
	} if(simplex()) cout << (long long)(-a[0][0] + 0.5);
	return 0;
}

附:随机扰动的代码

    bool init(){
        while(true){
            int l = 0, e = 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) return false; pivot(l, e);
        } return true;
    }
发布了610 篇原创文章 · 获赞 94 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/103414023