[Luogu P3980] [BZOJ 1061] [NOI2008]志愿者招募

版权声明:欢迎转载蒟蒻博客,但请注明出处:blog.csdn.net/lpa20020220 https://blog.csdn.net/LPA20020220/article/details/82413795

洛谷传送门

BZOJ传送门

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:

3 3
2 3 4
1 2 2
2 3 5
3 3 2

输出样例#1:

14

说明

1 N 1000 1 M 10000 ,题目中其他所涉及的数据均 不超过 2 31 1

解题分析

网络流神题, 好像这种类似线性规划的题都可以这么搞?

例如一共需要 4 天,四天需要的人数依次是 4 , 2 , 6 , 3 。有 5 类志愿者,如下表所示:

种类 1 2 3 4 5
时间 1-2 2-2 2-3 3-3 3-4
费用 3 4 3 5 6

设第 i 天需要的人为 R [ i ] , 租借了第 i 种志愿者 N [ i ] 名, 其花费为 V [ i ] , 那么有以下等式:

N [ 1 ] R [ 1 ] N [ 1 ] + N [ 2 ] + N [ 3 ] R [ 2 ] N [ 3 ] + N [ 4 ] + N [ 5 ] R [ 3 ] N [ 5 ] R [ 4 ]

我们引入一个自由变量 Q [ i ] , 使不等式变成等式:
N [ 1 ] Q [ 1 ] = 4 N [ 1 ] + N [ 2 ] + N [ 3 ] Q [ 2 ] = 2 N [ 3 ] + N [ 4 ] + N [ 5 ] Q [ 3 ] = 6 N [ 5 ] Q [ 4 ] = 3

我们可以发现同一变量出现次数太多, 看起来很不爽, 所以我们设 R [ 0 ] = R [ 6 ] = 0 ,再将这些等式两两作差, 可以得到:
N [ 1 ] Q [ 1 ] 4 = 0 N [ 2 ] + N [ 3 ] + Q [ 1 ] Q [ 2 ] + 2 = 0 N [ 1 ] N [ 2 ] + N [ 4 ] + N [ 5 ] + Q [ 2 ] Q [ 3 ] 4 = 0 N [ 3 ] N [ 4 ] + Q [ 3 ] Q [ 4 ] + 3 = 0 N [ 5 ] + Q [ 4 ] + 3 = 0

这样我们发现每个式子结果都为0, 而且常数项之和也为0, 每个变量也正好出现两次, 一正一负。

看起来是不是很像网络流的流量平衡? 事实上我们可以把每个式子看为一个点,正常数、 正系数对应的变量为流出的流量, 负常数, 负系数对应的变量为流入的流量。设源点为 S ,汇点为 T

  • 如果在第 i 个式子中 N [ k ] 系数为 1 而第 j 个式子中 N [ k ] 系数为 1 , 那么连边 i j , 流量为 I N F , 花费为 V [ k ] 。事实上就是从 S k 连向 T k + 1
  • 如果在第 i 个式子中, 常数项 C [ i ] 为正, 那么从 i T 连一条流量为 C [ i ] , 费用为 0 的边, 否则从 S i 连一条流量为 C [ i ] , 费用为 0 的边。
  • 如果在第 i 个式子中, Q [ k ] 系数为正而在第 j 个式子中, Q [ k ] 系数为负, 那么连边 i j ,流量为 I N F ,费用为 0 。事实上就是从 i + 1 连向 i

这样跑一边最大流就可以满足所有等式, 又因为要使得费用最小, 所以跑最小费用最大流。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1050
#define S 0
#define T 1005
#define INF 100000000
template <class TT>
IN void in(TT &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
struct Edge {int to, fl, cost, nex;} edge[MX << 5];
int dot, cnt, typ, ans;
int head[MX], dis[MX], del[MX], pre[MX];
std::queue <int> q;
int cost[MX], lb[MX], rb[MX], req[MX];
bool inq[MX];
IN void add(R int from, R int to, R int fl, R int cost)
{
    edge[++cnt] = {to, fl, cost, head[from]}, head[from] = cnt;
    edge[++cnt] = {from, 0, -cost, head[to]}, head[to] = cnt;
}
namespace MCMF
{
    IN bool SPFA()
    {
        std::memset(dis, 63, sizeof(dis));
        dis[S] = 0; del[S] = INF; q.push(S); R int now;
        W (!q.empty())
        {
            now = q.front(); q.pop();
            for (R int i = head[now]; ~i; i = edge[i].nex)
            {
                if(edge[i].fl > 0 && dis[edge[i].to] > dis[now] + edge[i].cost)
                {
                    dis[edge[i].to] = dis[now] + edge[i].cost;
                    del[edge[i].to] = std::min(edge[i].fl, del[now]);
                    pre[edge[i].to] = i;
                    if(!inq[edge[i].to]) q.push(edge[i].to), inq[edge[i].to] = true;
                }
            }
            inq[now] = false;
        }
        return dis[T] < INF;
    }
    IN void updata()
    {
        ans += del[T] * dis[T];
        R int now = T, pr;
        W (now != S)
        {
            pr = pre[now];
            edge[pr].fl -= del[T];
            edge[pr ^ 1].fl += del[T];
            now = edge[pr ^ 1].to;
        }
    }
    IN void init() {W (SPFA()) updata();}
}
int main(void)
{
    int buf, a, b, c;
    std::memset(head, cnt = -1, sizeof(head));
    in(dot), in(typ);
    for (R int i = 1; i <= dot; ++i) in(req[i]);
    for (R int i = 0; i <= dot; ++i)
    {
        buf = req[i + 1] - req[i];
        if(buf > 0) add(S, i + 1, buf, 0);
        else add(i + 1, T, -buf, 0);
    }
    for (R int i = 1; i <= dot; ++i) add(i + 1, i, INF, 0);
    for (R int i = 1; i <= typ; ++i)
    in(a), in(b), in(c), add(a, b + 1, INF, c);
    MCMF::init();
    printf("%d", ans);
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/82413795