【BZOJ】1061: [Noi2008]志愿者招募

题解

可能是世界上最裸的一个单纯形
(话说全幺模矩阵是啥我到现在都不知道)

假装我们已经看过了算导,或者xxx的论文,知道了单纯形是怎么实现的
扔一个blog走掉。。https://www.cnblogs.com/ECJTUACM-873284962/p/7097864.html

那么我们根据题意可以列出这样的方程

\(x_i\)表示第\(i\)类志愿者招募的个数

根据题目可列线性规划的式子
以样例为例
\(z = min 2x_1 + 5x_2 + 2x_3\)
\(x_1 + 0 + 0 >= 2\)
\(x_1 + x_2 + 0 >= 3\)
\(0 + x_2 + x_3 >= 4\)
\(x_1,x_2,x_3 >= 0\)
显然,标准型要求我们这些式子是小于号并且z要取max
好吧,反号?
不过我们有个很神奇的原理叫对偶原理
(我是真的不知道为啥……)

也就是
\(min c^T X\)
\(Ax = b\)
等价于
\(max b^T X\)
\(A^TX = c\)

好吧,这是我们喜欢的形式啊
然后我们的方程就可以写成这个样子

\(z = max 2x_1 + 3x_2 + 4x_3\)
\(x_1 + x_2 + 0 <= 2\)
\(0 + x_2 + x_3 <= 5\)
\(0 + 0 + x_3 <= 2\)

我们把这个方程转换成松弛型(也就是全是等于号)
\(x_1 + x_2 + 0 + x_4 = 2\)
\(0 + x_2 + x_3 + x_5 = 5\)
\(0 + 0 + x_3 + x_6 = 2\)
我们3个方程组,6个元,是消不出来的,我们这是一些作为基变量,剩下的非基变量都设成0,那样的话一定是单纯形上的一个顶点

这是一个\(m * (n + m)\)的矩阵,有点大
我们在处理的时候,初始设定所有的\(x_4,x_5,x_6\)作为基变量

每一个方程就是一个关于基变量的等式,我们找到一个替入变量,找到能使替入变量值最大的方程组的等式,将替入变量的位置当做替出变量的位置,矩阵就是\(nm\)的了

代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#define enter putchar('\n')
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define eps 1e-7
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {putchar('-');x = -x;}
    if(x >= 10) {
        out(x / 10);
    }
    putchar('0' + x % 10);
}
const int MAXN = 1005;
const int MAXM = 10005;
int N,M;
db b[MAXM],c[MAXN],a[MAXM][MAXN],ans;
void pivot(int id,int p) {
    b[id] /= a[id][p];
    for(int j = 1 ; j <= N ; ++j) {
        if(j != p) a[id][j] /= a[id][p];
    }
    a[id][p] = 1 / a[id][p];
    for(int i = 1 ; i <= M ; ++i) {
        if(i != id) {
            if(fabs(a[i][p]) < eps) continue;
            for(int j = 1 ; j <= N ; ++j) {
                if(j != p) a[i][j] -= a[i][p] * a[id][j];
            }
            b[i] -= a[i][p] * b[id];a[i][p] = -a[i][p] * a[id][p];
        }
    }
    for(int j = 1 ; j <= N ; ++j) {
        if(j != p) c[j] -= c[p] * a[id][j];
    }
    ans += c[p] * b[id];c[p] = -c[p] * a[id][p];
}

void Init() {
    read(N);read(M);
    for(int i = 1 ; i <= N ; ++i) scanf("%lf",&c[i]);
    int s,t;
    for(int i = 1 ; i <= M ; ++i) {
        scanf("%d %d %lf",&s,&t,&b[i]);
        for(int j = s ; j <= t ; ++j) {
            a[i][j] = 1.0;
        }
    }
}
void Solve() {
    while(1) {
        db t = -1;int p = 0;
        for(int i = 1 ; i <= N ; ++i) {
            if(c[i] > t) {
                t = c[i];p = i;
            }
        }
        if(t <= 0) {printf("%.0lf\n",ans);return;}
        t = 0x5fffffff;int id = 0;
        for(int j = 1 ;j <= M ; ++j) {
            if(a[j][p] > 0 && b[j] / a[j][p] < t) {
                t = b[j] / a[j][p];
                id = j;
            }
        }
        pivot(id,p);
    }
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Init();
    Solve();
}

猜你喜欢

转载自www.cnblogs.com/ivorysi/p/9195521.html