线性规划

十级考点线性规划。一开始打算把单纯形写了跑路的,到后面发现这是个大坑。填了得了。

线性规划的定义

线性规划是一个需要最大/小化某个受限于有限个线性约束(线性等式和线性不等式)的线性函数的问题。

线性规划的两种规范形式

我们常用两种形式表示线性规划。

  1. 标准型:最大化 \(\sum_{i=1}^nc_ix_i\),满足以下约束

\[\sum_{j=1}^na_{ij}x_j\le b_i\qquad i=1,2,\cdots,m\\ x_i\ge 0\qquad i=1,2,\cdots,m \]

  1. 松弛型:最大化 \(\sum_{i=1}^nc_ix_i\),满足以下约束

\[\sum_{j=1}^na_{ij}x_j=b_i\qquad i=1,2,\cdots,m\\ x_i\ge 0\qquad i=1,2,\cdots,m \]

对于一个一般的线性规划都可以转化为标准型或松弛型。有如下方法:

  1. 对于最小化问题,可以取反然后变成最大化。
  2. 对于等式,可以变成一个大于等于和一个小于等于。
  3. 对于不等式,可以建立变量 \(y\ge 0\) ,把原不等式变成 \(\sum_{j=1}^na_{ij}x_j\pm y=b_i\)即可。

单纯形算法求解线性规划

我们现在只要切掉uoj上的板子。数据还挺强的。我们通常用实现简单的单纯形法解决问题。

首先这玩意是个指数级的算法。然后这玩意期望下一秒可以跑几百,够用了。

单纯形法输入一个标准型的线性规划,返回“无解”,“无界”或一个取得最优解的方案。线性规划其实有个几何意义,就是在 \(n\) 维空间内,每个不等式构成一个 \(n-1\) 维超平面,这个凸包上使取值达到最值的点。单纯形法执行如下步骤:

  1. 将标准型转化为松弛型
  2. 初始化,找到初始解,判断无解
  3. 最优化,判断有界或返回解

下面是每一个步骤。

转化为松弛型

新建 \(m\) 个变量 \(x_{n+1},x_{n+2},\cdots,x_{n+m}\) ,并使得 \(x_{n+i}=b_i-\sum_{j=1}^na_{ij}x_j,x_{n+i}\ge 0\)。那么原线性规划就变成了如下形式的松弛型:

\[\begin{aligned} x_{n+i}=b_i-\sum_{j=1}^na_{ij}x_j\qquad &i=n+1,n+2,\cdots,n+m\\ x_i\ge 0\qquad &i=1,2,\cdots n+m \end{aligned} \]

我们将 \(x_1,x_2,\cdots,x_n\) 称为非基变量,\(x_{n+1},x_{n+2},\cdots,x_{n+m}\) 称为基变量。我们只要把所有的非基变量设置成 \(0\) ,基变量设置为 \(b_i\) 就可以得到一组解。显然所有的非基变量都可以由基变量表示。那么单纯形法不断选择不同的基变量集合表示线性规划,当所有的 \(c_i\) 都是非正数(即无法再增大)就得到了解。

转轴操作

转轴将一个非基变量和一个基变量交换。\(\text{pivot}(l,e)\) 交换两个变量 \(x_{n+l}\)\(x_e\) 的位置。

假设一开始有 \(x_{n+l}=b_l-\sum_{j=1}^na_{ij}x_j\)

移项有 \(a_{ie}x_e=b_l-\sum_{j\neq e}a_{ij}x_j-x_{n+l}\),也就是

\[x_e=\frac{b_l-\sum_{j\neq e}a_{ij}x_j-x_{n+l}}{a_{ie}} \]

用这个等式替换所有 \(x_e\) 即可。

初始化

为了初始化,我们需要一个辅助线性规划:最小化 \(x_0\) ,满足

\[\begin{aligned} \sum_{j=1}^na_{ij}x_j\le b_i+x_0\qquad &i=1,2,\cdots,m\\ x_i\ge 0\qquad &i=0,1,\cdots,n \end{aligned} \]

显然只有 \(x_0=0\) 时原线性规划有解。

转化为松弛型后找到 \(b_l\) 最小的 \(l\) ,执行 \(\text{pivot}(l,0)\) 即可得到初始解。然后最优化(这个一会说),即可解除辅助线性规划的最优解,判断是否有解。然后去掉 \(x_0\) ,继续最优化即可。

最优化

最优化每次选取一个增大后能使得状态变优(答案不一定增加)的变量,将它增大到有基变量为 \(0\) ,然后交换它们的位置。即不断执行:选取满足 \(c_e\ge 0\) 的非基变量 \(x_e\)。找到 \(a_{le}>0,\dfrac {b_l}{a_{le}}\) 最小的基变量 \(x_{n+l}\),转轴,直到找不到 \(c_e\ge 0\) 的非基变量。

为了减少转轴次数,每次我们选取能使答案增大最多的进行转轴。

有两种终止情况:找不到 \(e\) ,则最优解;找不到 \(l\),则无界。

以下是一份跑的很慢的板子。不知道为啥这么慢。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
int n,m,r[50],id[50];
const __float128 eps=1e-20;
__float128 a[50][50];
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
void pivot(int x,int y){
    swap(r[x+n],r[y]);
    __float128 k=-a[x][y];a[x][y]=-1/k;
    for(int i=0;i<=n;i++)if(i!=y)a[x][i]/=k;
    for(int i=0;i<=m;i++){
        if(i!=x&&(a[i][y]<=-eps||a[i][y]>=eps)){
            __float128 ret=a[i][y];a[i][y]=ret*a[x][y];
            for(int j=0;j<=n;j++)if(j!=y)a[i][j]+=a[x][j]*ret;
        }
    }
}
bool init(){
    while(1){
        int mn=1;
        for(int i=2;i<=m;i++)if(a[i][0]<a[mn][0])mn=i;
        if(a[mn][0]+eps>0)return true;
        int mx=-1;
        for(int i=1;i<=n;i++){
            if(a[mn][i]>=eps&&(mx==-1||r[mx]<r[i]))mx=i;
        }
        if(~mx)pivot(mn,mx);
        else return false;
    }
}
bool simplex(){
    while(1){
        int mx=1;
        for(int i=2;i<=n;i++){
            if(a[0][i]>a[0][mx])mx=i;
        }
        if(a[0][mx]<eps)return true;
        int mn=-1;__float128 ret=1e20;
        for(int i=1;i<=m;i++){
            if(a[i][mx]+eps<=0){
                __float128 tmp=-a[i][0]/a[i][mx];
                if(mn==-1||(r[mn]>r[i]?ret>tmp:ret>=tmp))mn=i,ret=tmp;
            }
        }
        if(~mn)pivot(mn,mx);
        else return false;
    }
}
int main(){
    int od;
    n=read();m=read();od=read();
    for(int i=1;i<=n;i++)a[0][i]=read();
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++)a[i][j]=-read();
        a[i][0]=read();
    }
    for(int i=1;i<=n+m;i++)r[i]=i;
    if(!init()){
        puts("Infeasible");return 0;
    }
    if(!simplex()){
        puts("Unbounded");return 0;
    }
    printf("%.10Lf\n",(long double)a[0][0]);
    if(od){
        for(int i=n+1;i<=n+m;i++)id[r[i]]=i;
        for(int i=1;i<=n;i++)printf("%.10Lf ",id[i]?(long double)a[id[i]-n][0]:0);
    }
    return 0;
}

一些可以转化为线性规划的问题

最短路

(这玩意就是闲着没事干)

显然我们有如下的线性规划:最小化 \(dis_t\) ,满足:

\[dis_s=0\\ dis_{v_i}\le dis_{u_i}+w_i \]

最大流

这个还是有用处的(特别是你想不出网络流建模的时候上一个不带脑子的线性规划可以快速破解,就是空间大点)

\(f_{ij}\)\(i\)\(j\) 的实际流量,那么有如下线性规划:最大化 \(\sum f_{Si}\),且满足:

\[f_{uv}\le w_{uv}\\ f_{uv}=-f_{vu}\\ \sum f_{ui}=0 \]

费用流

首先跑出最大流。然后设对于边 \((u,v)\) ,流量为 \(f_{uv}\),容量 \(c_{uv}\) ,代价 \(w_{uv}\)\(u\) 点的流出流量减流进流量为 \(b_u\) 。那么有如下线性规划:最小化 \(\sum f_{uv}w_{uv}\),满足

\[f_{uv}\le c_{uv}\\ f_{uv}=-f_{vu}\\ \sum f_{uv}=b_u \]

多商品流

多商品流是这样的问题:给你一个 \(n\) 个点有向图,边有流量限制 \(w\)。有 \(p\) 条路径,第 \(i\) 条从 \(s_i\)\(t_i\) 运送 \(d_i\) 的流量,求是否存在可行解。

这个似乎只能线性规划搞。设 \(pn^2\) 个变量 \(f_{k,i,j}\) 表示商品 \(k\)\(i\)\(j\) 的实际流量。则有:

\[\sum_kf_{k,u,v}\le w_{u,v}\\ f_{k,u,v}=-f_{k,v,u}\\ \sum f_{k,u,i}=0\\ \sum f_{k,s_i,t_i}=d_i \]

找可行解即可。

线性规划的对偶性

线性规划 \(\{\max c^Tx|Ax\le b,x_i\ge 0\}\) 的最优解和 \(\{\min b^Tx|A^Tx\ge c,x_i\ge 0\}\) 的最优解相同。

一个应用例:[NOI2008] 志愿者招募

题意不再赘述。容易列出线性规划式子:最小化 \(\sum_{i=1}^mc_ix_i\) ,满足

\[\sum_{l_j\le i\le r_j}x_j\ge a_i\\ x_i\ge 0 \]

线性规划转对偶后即可求解。同时对偶的一个可行解是全 \(0\) ,不需要初始化。

暂时没了?dxm的论文等我看懂了再补上。

posted @ 2022-12-28 17:58  gtm1514  阅读(249)  评论(0编辑  收藏  举报