[單純形法與線性規划]【學習筆記】


很早以前學過理論,3個月前又學了一遍寫了一點筆記,現在覺得以(已)前(經)寫(完)的(全)太(忘)丑(記)於是重寫一遍

參考資料:

1.算法導論

2.2016國家集訓隊論文


 

標准型

$Maximize\quad \sum\limits_{j=1}^{n} c_jx_j$

$Satisfy\quad constraint:$

$\sum\limits_{j=1}^{n} a_{ij}x_j \le b_i,\ i=1,2,...,m$

$x_j \ge 0,\ j=1,2,...,n$

 

$n$個變量,$m+n$個約束

構造$m*n$的矩陣$A$,$m$維向量$b$,$n$維向量$c$

 

$Maximize\quad c^Tx$

$Satisfy\quad constraint:$

$Ax \le b$

$x \ge 0$

 

轉化為標准型:

 

 

松弛型

松弛變量$x_{n+i}$

$ \sum\limits_{j=1}^{n} a_{ij}x_j \le b_i\  \rightarrow\  $$x_{n+i}=b_i - \sum\limits_{j=1}^{n} a_{ij}x_j,\ x_{n+i} \ge 0$

等式左側為基本變量,右側為非基本變量

 


 

 

單純型算法

每個約束定義了$n$維空間中的一個半空間(超平面),交集形成的可行域是一個凸區域稱為單純型

目標函數是一個超平面,最優解在凸區域定點處取得

 

基本解:非基本變量值為$0$,基本變量為右側的常數

基本可行解:所有$b_i \ge 0$

通過不斷的轉軸操作,在$n$維凸區域的頂點上不斷移動(轉軸),使得基本解的目標值不斷變大,最終達到最優解

 

轉軸:

選取一個非基本變量$x_e$為替入變量,基本變量$x_l$為替出變量,將其互換

為了防止循環,根據$Bland$規則,選擇下標最小的變量

 

初始化:

算法導論上有一個輔助線性規划的做法

但我發現好多人都用了隨機初始化的黑科技

在所有$b_i < 0$的約束中隨機選一個作為$x_l$,再隨機選一個$a_{le} < 0$的作為$x_e$,然后$Pivot(l,e)$后$b_i$就變正了...

 



代碼實現:

直接用一個$a[][]$來保存目標函數和約束

$Pivot$里的各種操作推導一下很清楚,用了兩個$trick$避免了一些判斷

$id$用來保存基本變量和非基本變量集合

 

針對全幺模矩陣可以進行提取非零系數的優化

UOJ#179. 線性規划

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=25;
const double eps=1e-8,INF=1e15;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}
int n,m,type;
double a[N][N],ans[N];
int id[N<<1];
int q[N];
void Pivot(int l,int e){
    swap(id[n+l],id[e]);
    double t=a[l][e]; a[l][e]=1;
    for(int j=0;j<=n;j++) a[l][j]/=t;
    for(int i=0;i<=m;i++) if(i!=l && abs(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 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) {puts("Infeasible");return false;}
        Pivot(l,e);
    }
    return true;
}
bool simplex(){
    while(true){
        int l=0,e=0; double mn=INF;
        for(int j=1;j<=n;j++) 
            if(a[0][j]>eps) {e=j;break;}
        if(!e) break;
        for(int i=1;i<=m;i++) if(a[i][e]>eps && a[i][0]/a[i][e]<mn)
            mn=a[i][0]/a[i][e],l=i;
        if(!l) {puts("Unbounded");return false;}
        Pivot(l,e);
    }
    return true;
}
int main(){
    freopen("in","r",stdin);
    srand(317);
    n=read();m=read();type=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;i++) id[i]=i;
    if(init() && simplex()){
        printf("%.8lf\n",-a[0][0]);
        if(type){
            for(int i=1;i<=m;i++) ans[id[n+i]]=a[i][0];
            for(int i=1;i<=n;i++) printf("%.8lf ",ans[i]);
        }
    }
}
View Code

 


 

 

 

問題轉化為單純型:

最短路

最大流

最小費用最大流

多商品流(目前沒寫過)

 

對偶性:

${Max\ c^Tx\ :\ Ax \le b,\ x \ge 0}\ \quad {Min b^Ty\ :\ A^Ty \ge c,\ t \ge 0}$

最大化與最小化互換,常數與目標函數互換,改變不等號,變量與約束對應

最大流與最小割

二分圖最大權匹配與最小頂標和

最小頂標和:一個帶權二分圖,兩個頂點的頂標之和不小於連接它們的邊的邊權,求最小頂標和

所有邊權為$1$,就是最大匹配和最小點覆蓋

 

$d_{uv}$表示$u,v$是否匹配

$Max\quad \sum\limits_{(u,v)\in E}c_{uv}d_{uv}$

$Sat$

$\sum\limits_{v \in Y}d_{uv} \le 1 \quad \quad u \in X$

$\sum\limits_{u \in X}d_{uv} \le 1 \quad \quad v \in Y$

$d_{u,v}\in \{0,1\}$

 

令$p_u,p_v$為兩類約束對偶之后的變量


$Min\quad \sum\limits_{u \in X}p_u + \sum\limits_{v \in Y}p_v$

$Sat$

$p_u+p_v \ge c_{uv} \quad \quad u \in X,v \in Y$

$p_u,p_v \ge 0$

 

 

全幺模矩陣(totally unimodular matrix)

充分條件:

1.僅有$-1,0,1$構成

2.每列至多兩個非零數

3.行可分為兩個集合:

一列包含兩個同號非零數,兩行不在同一個集合

一列包含兩個異號非零數,兩行在同一個集合

 

線性規划中$A$為全幺模矩陣,則單純形法過程中所有系數$\in{-1,0,1}$

可以去除系數為$0$的項進行優化!

任何最大流、最小費用最大流的線性規划都是全幺模矩陣

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM