[BZOJ 1061][Noi2008]志愿者招募

Description

申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。

Input

第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含N 个非负整数,表示每天至少需要的志愿者人数。 接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了方便起见,我们可以认为每类志愿者的数量都是无限多的。

Output

仅包含一个整数,表示你所设计的最优方案的总费用。

Solution

设每类志愿者招募了xi名
按照每天的约束来列不等式,表示第i天>=ci
最小化费用
可是我们发现0并不是一个初始解
对偶一下
变成最大化费用。<=
单纯形即可

#include <bits/stdc++.h>

using namespace std;

int n, m;

const double inf = 1e12;

struct solver{
    double a[10010][1010], b[10010], c[1010], v;
    void Debug(){
        printf("Max : ");
        for(int i = 1; i <= n; i ++)
            printf("%.2lf ", c[i]);
        printf("%.2lf\n", v);
        for(int i = 1; i <= m; i ++){
            for(int j = 1; j <= n; j ++)
                printf("%.2lf ", a[i][j]);
            printf("%.2lf\n", b[i]);
        }
        system("pause");
    }
    void pivot(int f, int e){
        b[f] /= a[f][e];
        for(int i = 1; i <= n; i ++)
            if(i != e)a[f][i] /= a[f][e];
        a[f][e] = 1 / a[f][e];

        for(int i = 1; i <= m; i ++){
            if(i == f || a[i][e] == 0)continue;
            b[i] -= a[i][e] * b[f];
            for(int j = 1; j <= n; j ++)
                if(j != e)a[i][j] -= a[i][e] * a[f][j];
            a[i][e] = -a[i][e] * a[f][e];
        }

        v += c[e] * b[f];
        for(int i = 1; i <= n; i ++)
            if(i != e)c[i] -= c[e] * a[f][i];
        c[e] = -c[e] * a[f][e];
    }

    double solve(){
        int e, i, f;
        while(true){
            for(e = 0, i = 1; i <= n; i ++)
                if(c[i] > 0){e = i; break;}
            if(e == 0)return v;
            double lim = inf;
            for(i = 1; i <= m; i ++){
                if(a[i][e] > 0 && lim > b[i] / a[i][e]){
                    lim = b[i] / a[i][e];
                    f = i;
                }
            }
            pivot(f, e);
        }
    }
}spx;

int main(){
    scanf("%d%d", &n, &m);
    int l, r, x;
    for(int i = 1; i <= n; i ++){
        scanf("%d", &x);
        spx.c[i] = x;
    }
    for(int i = 1; i <= m; i ++){
        scanf("%d%d", &l, &r);
        for(int j = l; j <= r; j ++)
            spx.a[i][j] = 1;
        scanf("%d", &x);
        spx.b[i] = x;
    }
    printf("%d", (int)(spx.solve() + 0.1));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值