【线性规划】Volunteer recruitment

Description

Suppose you will recruit a group of volunteers for a coming event. It is estimated that this event will take N days to complete, and the i(th) day needs at least Ai volunteers. The number of kinds of volunteers is M. The volunteers of i(th) kind can volunteer from the Si day to the Fi day and the recruit fee is Ci. In order to do his job well, you hope to recruit enough volunteers with least money. Please give an optimal plan.

note: i(th) means the ordinal of this number is i

Input

The first line presents two integers: N, M. The second line contains N nonnegative integers presenting the numbers of volunteers each day needs. Each of the other lines contain three integers: Si, Fi, Ci

note: You can recruit each kind of volunteers as many as possible.

Output

Just a number meaning the total money of your optimal plan.

Sample Input

3 3

2 3 4

1 2 2

2 3 5

3 3 2

Sample Output

14

Hint

1<=N<=200, 1<=M<=1000, 题目中所有数据不超过2^31-1

原题地址:

UOJ#30

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef struct BaseVec{
    int vec_col;   // 每个基向量在表中的列索引
    int val_1_row; // 基向量值为1对应的行索引,方便得出最优解后直接乘b,无需矩阵乘法
}BaseVec;
class Solution{
public:
    // 需要判断 行数N(b的长度)是否>列数M,如果大于则需要求对偶问题,否则基向量长度≠b的长度
    float simplex(vector<float>& c, vector<vector<float>>& A, float b[]){
        N = A.size();      // N表示约束矩阵的行数
        M = A[0].size();   // M表示变量x的个数,在约束矩阵为列数
        BaseVec BI[N];     // 基向量索引
        // 1.找初始基向量,即找初始多胞形顶点
        initalizeSimplex(c, A, b, BI);
        while (true){
            // 2.找c为负数对应的列
            c_no_negative = true;
            for(int j = 0;j < M;j++)
                if(c[j] < 0){
                    negColIndex = j;
                    c_no_negative = false;
                    break;
                }
            // 3.1 如果c全为正数,则得到最优解
            if(c_no_negative){
                return opt;
            }else{  // 3.2 如果c有负数,则需要高斯行变换
                // 找到colIndex这一列中b/λ最小的一行minRowIndex
                minRatio = 1e100;
                for(int i = 0;i < N;i++)
                    if(A[i][negColIndex] > 0 && (b[i]/A[i][negColIndex]) < minRatio){  // 只有>0才能比较
                        minValue = A[i][negColIndex];
                        minRatio = b[i] / minValue;
                        minRowIndex = i;
                    }
                pivot(c, A, b, BI);  // 以minRowIndex为行,negColIndex为列进行高斯行变换
            }
        }

    }
private:
    int N, M, minRowIndex, negColIndex;       // -z = Cb*B^(-1)*b,
    bool c_no_negative;
    float minRatio, minValue, factor, c_facter, opt;

    void pivot(vector<float>& c, vector<vector<float>>& A, float b[], BaseVec BI[]) {
        // 1.pivot自己这一行(包括b对应值)先逐个除以pivot元素,以保证pivot=1
        c_facter = c[negColIndex];
        for (int j = 0; j < M; j++){
            A[minRowIndex][j] /= minValue;
            // 2.3 c系数向量元素高斯消元
            c[j] -= c_facter * A[minRowIndex][j];
        }
        b[minRowIndex] /= minValue;
        // 2.4 最优值opt=z高斯消元
        opt -= c_facter * b[minRowIndex];
        // 2.高斯行变换
        for (int i = 0; i < N; i++){
            // 2.1 A矩阵元素高斯消元
            if((A[i][negColIndex] != 0) && (i != minRowIndex)){ // 只要pivot对应行的值不为0且不是自己就进行消元
                factor = A[i][negColIndex];        // 每行的高斯消元乘子
                for(int j = 0;j < M;j++)
                    A[i][j] -=  factor * A[minRowIndex][j];  // 逐个相减
                // 2.2 b矩阵元素高斯消元
                b[i] -= factor * b[minRowIndex];
            }
            // 3.入新基,出旧基
            if(BI[i].val_1_row == minRowIndex)
                BI[i].vec_col = negColIndex;
        }
    }
    void initalizeSimplex(vector<float>& c, vector<vector<float>>& A, float b[], BaseVec BI[]){
        for(int i = 0;i < N;i++){
            for(int j = 0;j < N;j++)  // 1.扩充A,合并松弛变量对应N×N单位矩阵BI=E
                (i==j) ? A[i].push_back(1) : A[i].push_back(0);
            // 2.记录BI=E行和列索引
            BI[i].vec_col = M + i;
            BI[i].val_1_row = i;
            // 3.加入N个松弛变量,c也需要扩充N个0
            c.push_back(0);
        }
        M += N;
        opt = 0;               // 初始化z=0
    }
};
int main(){
    int N, M, S, F;
    cin >> N >> M;
    // 将原问题转化为对偶问题 N->M
    vector<float> c(N);
    float b[M];
    vector<vector<float>> A(M, vector<float>(N));
    A.resize(M, vector<float>(N));
    for(int j = 0;j < N;j++){
        scanf("%f", &c[j]);
        c[j] = -c[j];
    }
    for(int i = 0;i < M;i++){
        cin >> S >> F >> b[i];
        for(int j = 0;j < N;j++)
            if ((j+1 - S >= 0)&&(j+1 - F <= 0))
                A[i][j] = 1;
    }
    Solution sol;
    cout<<sol.simplex(c, A, b)<<endl;
    return 0;
}
发布了126 篇原创文章 · 获赞 438 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/SL_World/article/details/103658441