[BZOJ1061][Noi2008]志愿者招募
[BZOJ1061][Noi2008]志愿者招募
试题描述
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难
题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要
Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用
是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这
并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。
输入
第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含N 个非负
整数,表示每天至少需要的志愿者人数。 接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了
方便起见,我们可以认为每类志愿者的数量都是无限多的。
输出
仅包含一个整数,表示你所设计的最优方案的总费用。
输入示例
3 3 2 3 4 1 2 2 2 3 5 3 3 2
输出示例
14
数据规模及约定
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。
题解
NOI的题目就是好。。。既能学新姿势还能轻松A掉。。。
这是一道费用流的题目,但是它的解题思路是基于流量平衡的。这类题可以搜集题目中的限制条件列出等式,转化成 A - B = 0 的形式,那么将该等式作为一个节点,A 就是流入的流量,B 即为流出的流量。
详细题解:https://www.byvoid.com/blog/noi-2008-employee/
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *tail; inline char Getchar() { if(Head == tail) { int l = fread(buffer, 1, BufferSize, stdin); tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 1010 #define maxm 24012 #define oo 2147483647 #define ool 1ll << 60 #define LL long long struct Edge { int from, to, flow, cost; } ; struct ZKW { int n, m, s, t, head[maxn], next[maxm]; Edge es[maxm]; LL d[maxn]; bool vis[maxn]; LL ans, cost; void init(int nn) { n = nn; m = 0; memset(head, -1, sizeof(head)); return ; } void AddEdge(int a, int b, int c, int d) { es[m] = (Edge){ a, b, c, d }; next[m] = head[a]; head[a] = m++; es[m] = (Edge){ b, a, 0, -d }; next[m] = head[b]; head[b] = m++; return ; } bool BFS() { for(int i = 1; i <= n; i++) d[i] = ool; d[t] = 0; deque <int> Q; Q.push_front(t); while(!Q.empty()) { int u = Q.front(); Q.pop_front(); for(int i = head[u]; i != -1; i = next[i]) { Edge& e = es[i^1]; if(e.flow && d[e.from] > d[u] + e.cost) { d[e.from] = d[u] + e.cost; if(Q.empty() || d[e.from] <= d[Q.front()]) Q.push_front(e.from); else Q.push_back(e.from); } } } if(d[s] == ool) return 0; for(int i = 0; i < m; i++) es[i].cost += d[es[i].to] - d[es[i].from]; cost += d[s]; return 1; } int DFS(int u, int a) { if(u == t || !a){ ans += cost * a; return a; } int flow = 0, f; vis[u] = 1; for(int i = head[u]; i != -1; i = next[i]) { Edge& e = es[i]; if(!vis[e.to] && !e.cost && (f = DFS(e.to, min(a, e.flow)))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } int MaxFlow(int ss, int tt) { s = ss; t = tt; int flow = 0, f; while(BFS()) do { memset(vis, 0, sizeof(vis)); f = DFS(s, oo); flow += f; } while(f); return flow; } } sol; int main() { int n = read(), m = read(); sol.init(n + 3); int s = n + 2, t = n + 3; int lastA = 0, A; for(int i = 1; i <= n; i++) { A = read(); if(A - lastA > 0) sol.AddEdge(s, i, A - lastA, 0); else sol.AddEdge(i, t, lastA - A, 0); sol.AddEdge(i + 1, i, oo, 0); lastA = A; } sol.AddEdge(n + 1, t, A, 0); for(int i = 1; i <= m; i++) { int x = read(), y = read(), c = read(); sol.AddEdge(x, y + 1, oo, c); } sol.MaxFlow(s, t); printf("%lld\n", sol.ans); return 0; } /* 3 3 2 3 4 1 2 2 2 3 5 3 3 2 */
这个题还有一个不用线性规划的做法:我们跑可行流。
对于每一天,流量下界为每天需要的志愿者人数,然后对于一个从第 s 天到第 t 天的志愿者,从这个志愿者向第 s 天连边(容量无穷费用为该志愿者的费用),然后从第 t 天向该志愿者连边(容量无穷费用为 0)。
我的代码搞了一个小优化,每天没有拆点,而是把一条边看成一天,所以加边成环时会有一些小边界问题要考虑。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; #define rep(i, s, t) for(int i = (s); i <= (t); i++) #define dwn(i, s, t) for(int i = (s); i >= (t); i--) int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 11111 #define maxm 46010 #define oo 2147483647 #define ool (1ll << 60) #define LL long long struct Edge { int from, to, flow, cost; Edge() {} Edge(int _1, int _2, int _3, int _4): from(_1), to(_2), flow(_3), cost(_4) {} }; struct ZKW { int n, m, s, t, head[maxn], nxt[maxm]; LL cost, ans; Edge es[maxm]; LL d[maxn]; int Q[maxn*10], hd, tl; bool inq[maxn]; bool vis[maxn]; void init() { m = 0; memset(head, -1, sizeof(head)); return ; } void setn(int _) { n = _; return ; } void AddEdge(int a, int b, int c, int d) { es[m] = Edge(a, b, c, d); nxt[m] = head[a]; head[a] = m++; es[m] = Edge(b, a, 0, -d); nxt[m] = head[b]; head[b] = m++; return ; } bool BFS() { rep(i, 1, n) d[i] = ool; d[t] = 0; hd = tl = 0; Q[++tl] = t; inq[t] = 1; while(hd < tl) { int u = Q[++hd]; inq[u] = 0; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i^1]; if(d[e.from] > d[u] + e.cost && e.flow) { d[e.from] = d[u] + e.cost; if(!inq[e.from]) { inq[e.from] = 1; Q[++tl] = e.from; } } } } if(d[s] == ool) return 0; cost = d[s]; return 1; } int DFS(int u, int a) { if(u == t || !a) return ans += cost * a, a; if(vis[u]) return 0; vis[u] = 1; int flow = 0, f; for(int i = head[u]; i != -1; i = nxt[i]) { Edge& e = es[i]; if(d[e.to] == d[u] - e.cost && (f = DFS(e.to, min(a, e.flow)))) { flow += f; a -= f; e.flow -= f; es[i^1].flow += f; if(!a) return flow; } } return flow; } int MaxFlow(int _s, int _t) { s = _s; t = _t; int flow = 0, f; while(BFS()) do { memset(vis, 0, sizeof(vis)); f = DFS(s, oo); flow += f; } while(f); return flow; } } sol; #define maxdays 1010 #define maxvol 10010 int CntP; struct Point { int id; Point(): id(0) {} int p() { return id ? id : id = ++CntP; } } Days[maxdays], Vol[maxvol], S, T; int main() { int days = read(), vol = read(); sol.init(); rep(i, 1, days) { int lim = read(); sol.AddEdge(S.p(), Days[i+1].p(), lim, 0); sol.AddEdge(Days[i].p(), T.p(), lim, 0); sol.AddEdge(Days[i].p(), Days[i+1].p(), oo - lim, 0); } rep(i, 1, vol) { int s = read(), t = read(), c = read(); sol.AddEdge(Vol[i].p(), Days[s].p(), oo, c); sol.AddEdge(Days[t+1].p(), Vol[i].p(), oo, 0); } sol.setn(CntP); sol.MaxFlow(S.p(), T.p()); printf("%lld\n", sol.ans); return 0; }