[BZOJ1061][Noi2008]志愿者招募
[BZOJ1061][Noi2008]志愿者招募
試題描述
申奧成功后,布布經(jīng)過不懈努力,終于成為奧組委下屬公司人力資源部門的主管。布布剛上任就遇到了一個(gè)難
題:為即將啟動的奧運(yùn)新項(xiàng)目招募一批短期志愿者。經(jīng)過估算,這個(gè)項(xiàng)目需要N 天才能完成,其中第i 天至少需要 Ai 個(gè)人。 布布通過了解得知,一共有M 類志愿者可以招募。其中第i 類可以從第Si 天工作到第Ti 天,招募費(fèi)用 是每人Ci 元。新官上任三把火,為了出色地完成自己的工作,布布希望用盡量少的費(fèi)用招募足夠的志愿者,但這 并不是他的特長!于是布布找到了你,希望你幫他設(shè)計(jì)一種最優(yōu)的招募方案。輸入
第一行包含兩個(gè)整數(shù)N, M,表示完成項(xiàng)目的天數(shù)和可以招募的志愿者的種類。 接下來的一行中包含N 個(gè)非負(fù)
整數(shù),表示每天至少需要的志愿者人數(shù)。 接下來的M 行中每行包含三個(gè)整數(shù)Si, Ti, Ci,含義如上文所述。為了 方便起見,我們可以認(rèn)為每類志愿者的數(shù)量都是無限多的。輸出
僅包含一個(gè)整數(shù),表示你所設(shè)計(jì)的最優(yōu)方案的總費(fèi)用。
輸入示例
3 3 2 3 4 1 2 2 2 3 5 3 3 2輸出示例
14數(shù)據(jù)規(guī)模及約定
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,題目中其他所涉及的數(shù)據(jù)均 不超過2^31-1。
題解
NOI的題目就是好。。。既能學(xué)新姿勢還能輕松A掉。。。
這是一道費(fèi)用流的題目,但是它的解題思路是基于流量平衡的。這類題可以搜集題目中的限制條件列出等式,轉(zhuǎn)化成 A - B = 0 的形式,那么將該等式作為一個(gè)節(jié)點(diǎn),A 就是流入的流量,B 即為流出的流量。
詳細(xì)題解: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 */這個(gè)題還有一個(gè)不用線性規(guī)劃的做法:我們跑可行流。
對于每一天,流量下界為每天需要的志愿者人數(shù),然后對于一個(gè)從第 s 天到第 t 天的志愿者,從這個(gè)志愿者向第 s 天連邊(容量無窮費(fèi)用為該志愿者的費(fèi)用),然后從第 t 天向該志愿者連邊(容量無窮費(fèi)用為 0)。
我的代碼搞了一個(gè)小優(yōu)化,每天沒有拆點(diǎn),而是把一條邊看成一天,所以加邊成環(huán)時(shí)會有一些小邊界問題要考慮。
#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 longstruct 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 10010int 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; }?
轉(zhuǎn)載于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5474420.html
總結(jié)
以上是生活随笔為你收集整理的[BZOJ1061][Noi2008]志愿者招募的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IOS开发数据库篇--- sqlite常
- 下一篇: C 语言分支结构