P3980 [NOI2008] 志愿者招募

一、题目

二、思路

可以看出,这是一个线性规划问题。

由于它是最小化型线性规划,不是标准形式的。因此,可以利用线性规划对偶定理将其转化为一个最大化型线性规划

对偶问题的标准形式:

于是写个最单纯的单纯形法就可以了 (连 init() 都不用写~)。

具体地过程,就是寻找大于 0 的非基 (自由) 变量转化成基变量,然后转轴 (注意代码实现时和 Gauss 消元的区别),最后得到的就是最小花费。

三、代码

复制代码
#include <bits/stdc++.h>
#define N 1034
#define E 10034
using namespace std;

const double eps = 1e-8;

int n, e, l, r;
int i, j;

int id[N + E];
double m[E][N], b[E], *c = m[0], ans[N + E];
// 指针 *c,指向数组 m 的第一行

// 定义函数 pivot,用于执行单纯性算法的主要操作:进行主元素调整。
void pivot(int r, int c)
// 函数 pivot 的输入参数为 r 和 c,分别表示要进行主元素调整的行和列。
{
    int i, j;
    double coe = 1.0 / m[r][c];
    swap(id[n + r], id[c]);
    // 交换 id[n + r] 和 id[c],表示 c 成为了基变量,而 n + r 成为了非基变量。
    m[r][c] = 1.0;
    // 将主元素 m[r][c] 设为 1。
    for (j = 1; j <= n; ++j)
        m[r][j] *= coe; // 将第 r 行的所有元素都除以主元素 m[r][c]。
    b[r] *= coe;        // 将第 r 行的常数项 b[r] 也除以主元素 m[r][c]。
    for (i = 0; i <= e; ++i)
    { // 遍历所有行,进行主元素调整。
        if (i == r)
            continue; // 如果当前行已经是主元素所在的行,则跳过。
        coe = m[i][c];
        m[i][c] = 0.0;
        for (j = 1; j <= n; ++j)
            m[i][j] -= coe * m[r][j];
        b[i] -= coe * b[r];
        // 将当前行的常数项减去主元素所在行的常数项乘以系数。
    }
}

bool simplex()
// 定义函数 simplex,用于执行单纯性算法的主要流程。
{
    int i, j, basic, free;
    double G;
    for (;;)
    {
        basic = free = 0;
        G = INFINITY;
        for (i = 1; i <= n; ++i) // free (nonbasic) variable
            if (c[i] > c[free])
                free = i;
        if (!free)
            return true;
        for (j = 1; j <= e; ++j) // basic variable
            if (m[j][free] > eps && b[j] < G * m[j][free])
            {
                G = b[j] / m[j][free];
                basic = j;
            }
        if (!basic)
            return puts("Unbounded"), false;
        pivot(basic, free);
    }
}
// 函数 simplex 的作用是不断进行主元素调整,
// 直到找到最优解或者发现问题无解。在每次迭代中,
// 首先找到最大的非基变量 free,然后遍历所有基变量,
// 找到最小的比率 G 和对应的基变量 basic,进行主元素调整。
// 如果找不到基变量,则问题无解;如果不存在非基变量,则找到最优解,单纯性算法结束。
// 最后,主函数 main 的作用是输入数据,调用函数 simplex,输出最优解。

int main()
{
    scanf("%d%d", &n, &e);
    for (j = 1; j <= n; ++j)
        scanf("%lf", c + j);
    for (i = 1; i <= e; ++i)
    {
        scanf("%d%d%lf", &l, &r, b + i);
        for (j = l; j <= r; ++j)
            m[i][j] = 1.0;
    }
    if (simplex())
        printf("%.lf\n", -*b);
    system("pause");
    return 0;
}
复制代码

 

posted @   ImreW  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示