【線性規劃】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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章