线性规划(单纯型法)

问题模型

n n n个变量 x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn m m m个约束条件 ∑ k i x i = b i \sum k_ix_i= b_i kixi=bi

题目要求所有的解非负,即 x i ≥ 0 x_i\geq 0 xi0

求解目标方程 ∑ k i x i \sum k_ix_i kixi的最大值。

模型转化

遇到 x 1 + x 2 ≤ 7 x_1+x_2\leq 7 x1+x27,可以增加一个非负松弛变量 x 3 x_3 x3,变为: x 1 + x 2 + x 3 = 7 , x 3 ≥ 0 x_1+x_2+x_3=7,x_3\geq 0 x1+x2+x3=7,x30

遇到 x 1 + x 2 ≥ 7 x_1+x_2\geq 7 x1+x27,可以增减去一个非负松弛变量 x 3 x_3 x3,变为: x 1 + x 2 − x 3 = 7 , x 3 ≥ 0 x_1+x_2-x_3=7,x_3\geq 0 x1+x2x3=7,x30

x 1 + x 2 x_1+x_2 x1+x2的最小值,可以取反,变为求 − x 1 − x 2 -x_1-x_2 x1x2的最大值。

无约束的变量 − ∞ ≤ x 1 ≤ ∞ -\infty\leq x_1\leq\infty x1,转化为两个变量的差: x 1 = x 2 − x 3 , x 2 ≥ 0 , x 3 ≥ 0 x_1=x_2-x_3,x_2\geq 0,x_3\geq 0 x1=x2x3,x20,x30

求解

先找出 m ∗ n m*n mn矩阵的基(行列式非0的 m ∗ m m*m mm子矩阵),令其它变量为0,若解出的基解满足约束条件( ≥ 0 \geq 0 0),则为基可行解。有定理:最优解一定是某个基可行解。

代码

original link - http://uoj.ac/problem/179

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 50
using namespace std;
const double eps=1e-9;
double a[N][N],ans[N];
int n,m,t,id[N],d[N];
int rd(int k) {
    return rand()%k;
}
void pivot(int l,int e) {
    swap(id[l+n],id[e]);
    double t=a[l][e];
    a[l][e]=1;
    fo(i,0,n) a[l][i]/=t;
    fo(i,0,m) {
        if(i!=l&&abs(a[i][e])>eps) {
            double t=a[i][e];
            a[i][e]=0;
            fo(j,0,n) a[i][j]-=t*a[l][j];
        }
    }
}
bool prp() {
    while(1) {
        int l=0,e=0;
        d[0]=0;
        fo(i,1,m) if(a[i][0]<-eps)
            d[++d[0]]=i;
        if(!d[0])
            return 1;
        l=d[rd(d[0])+1],d[0]=0;
        fo(i,1,n) if(a[l][i]<-eps)
            d[++d[0]]=i;
        if(!d[0])
            return 0;
        e=d[rd(d[0])+1];
        pivot(l,e);
    }
}
bool solve() {
    while(1) {
        int l=0,e=0;
        double mi=1e18;
        fo(i,1,n)
        if(a[0][i]>eps&&(!e||a[0][i]>a[0][e]))
            e=i;
        if(!e)
            return 1;
        fo(i,1,m) {
            if(a[i][e]>eps&&a[i][0]/a[i][e]<mi)
                mi=a[i][0]/a[i][e],l=i;
        }
        if(!l)
            return 0;
        pivot(l,e);
    }
}
int main() {
    srand(time(0));
    cin>>n>>m>>t; // n变量 m约束
    fo(i,1,n) {
        int x;
        scanf("%d",&x);
        a[0][i]=x; // 目标方程
    }
    fo(i,1,m) { // 约束 aij xj <= bi
        int x;
        fo(j,1,n) scanf("%d",&x),a[i][j]=x;
        scanf("%d",&x),a[i][0]=x;
    }
    fo(i,1,n+m) id[i]=i;
    if(!prp()) { // 无可行域
        printf("Infeasible\n");
        return 0;
    }
    if(!solve()) { // 无界,取得inf
        printf("Unbounded\n");
        return 0;
    }
    printf("%.10lf\n",-a[0][0]);
    if(t==1) {
        fo(i,1,m) ans[id[i+n]]=a[i][0];
        fo(i,1,n) printf("%.10lf ",ans[i]);
    }
}

references

https://www.bilibili.com/video/av15081451/
https://oi-wiki.org/math/simplex/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值