洛谷传送门
BZOJ传送门
题目描述
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要 天才能完成,其中第i 天至少需要 个人。 布布通过了解得知,一共有 类志愿者可以招募。其中第 类可以从第 天工作到第 天,招募费用是每人 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。
输入输出格式
输入格式:
第一行包含两个整数 , ,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含 个非负整数,表示每天至少需要的志愿者人数。 接下来的 行中每行包含三个整数 ,含义如上文所述。为了方便起见,我们可以认为每类志愿者的数量都是无限多的。
输出格式:
仅包含一个整数,表示你所设计的最优方案的总费用。
输入输出样例
输入样例#1:
3 3
2 3 4
1 2 2
2 3 5
3 3 2
输出样例#1:
14
说明
,题目中其他所涉及的数据均 不超过 。
解题分析
网络流神题, 好像这种类似线性规划的题都可以这么搞?
例如一共需要 天,四天需要的人数依次是 。有 类志愿者,如下表所示:
种类 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
时间 | 1-2 | 2-2 | 2-3 | 3-3 | 3-4 |
费用 | 3 | 4 | 3 | 5 | 6 |
设第
天需要的人为
, 租借了第
种志愿者
名, 其花费为
, 那么有以下等式:
我们引入一个自由变量 , 使不等式变成等式:
我们可以发现同一变量出现次数太多, 看起来很不爽, 所以我们设 ,再将这些等式两两作差, 可以得到:
这样我们发现每个式子结果都为0, 而且常数项之和也为0, 每个变量也正好出现两次, 一正一负。
看起来是不是很像网络流的流量平衡? 事实上我们可以把每个式子看为一个点,正常数、 正系数对应的变量为流出的流量, 负常数, 负系数对应的变量为流入的流量。设源点为 ,汇点为 。
- 如果在第 个式子中 系数为 而第 个式子中 系数为 , 那么连边 , 流量为 , 花费为 。事实上就是从 连向 。
- 如果在第 个式子中, 常数项 为正, 那么从 向 连一条流量为 , 费用为 的边, 否则从 向 连一条流量为 , 费用为 的边。
- 如果在第 个式子中, 系数为正而在第 个式子中, 系数为负, 那么连边 ,流量为 ,费用为 。事实上就是从 连向 。
这样跑一边最大流就可以满足所有等式, 又因为要使得费用最小, 所以跑最小费用最大流。
代码如下:
#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);
}