志愿者招募 HYSBZ - 1061(公式建图费用流)
Posted wtsruvf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了志愿者招募 HYSBZ - 1061(公式建图费用流)相关的知识,希望对你有一定的参考价值。
转自神犇:https://www.cnblogs.com/jianglangcaijin/p/3799759.html
题意:申奥成功后,布布经过不懈努力,终于 成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最 优的招募方案。
思路:
这个一个类影响一个区间,所以并不能像HDU - 3572 一样 按时间拆点
所以列出公式求解
例如一共需要4天,四天需要的人数依次是4,2,5,3。有5类志愿者,如下表所示:
设雇佣第i类志愿者的人数为X[i],每个志愿者的费用为V[i],第j天雇佣的人数为P[j],则每天的雇佣人数应满足一个不等式,如上表所述,可以列出
P[1]=X[1]+X[2]>=4
P[2]=X[1]+X[3]>=2
P[3]=X[3]+X[4]+X[5]>=5
P[4]=X[5]>=3
对于第i个不等式,添加辅助变量Y[i](Y[i]>=0),可以使其变为等式
P[1]=X[1]+X[2]-Y[1]=4
P[2]=X[1]+X[3]-Y[2]=2
P[3]=X[3]+X[4]+X[5]-Y[3]=5
P[4]=X[5]-Y[4]=3
在上述四个等式上下添加P[0]=0,P[5]=0,每次用下边的式子减去上边的式子,得出
① P[1]-P[0]=X[1]+X[2]-Y[1]=4
② P[2]-P[1]=X[3]-X[2]-Y[2]+Y[1]=-2
③ P[3]-P[2]=X[4]+X[5]-X[1]-Y[3]+Y[2]=3
④ P[4]-P[3]=-X[3]-X[4]+Y[3]-Y[4]=-2
⑤ P[5]-P[4]=-X[5]+Y[4]=-3
观察发现,每个变量都在两个式子中出现了,而且一次为正,一次为负.所有等式右边和为0.我们将最后的五个等式进一步变形,得出以下结果
① -X[1]-X[2]+Y[1]+4=0
② -X[3]+X[2]+Y[2]-Y[1]-2=0
③ -X[4]-X[5]+X[1]+Y[3]-Y[2]+3=0
④ X[3]+X[4]-Y[3]+Y[4]-2=0
⑤ X[5]-Y[4]-3=0
可 以发现,每个等式左边都是几个变量和一个常数相加减,右边都为0,恰好就像网络流中除了源点和汇点的顶点都满足流量平衡。每个正的变量相当于流入该顶点的 流量,负的变量相当于流出该顶点的流量,而正常数可以看作来自附加源点的流量,负的常数是流向附加汇点的流量。因此可以据此构造网络,求出从附加源到附加 汇的网络最大流,即可满足所有等式。而我们还要求费用最小,所以要在X变量相对应的边上加上权值,然后求最小费用最大流。
接下来,根据上面五个等式构图。
(1)每个等式为图中一个顶点,添加源点S和汇点T。
(2)如果一个等式中的数字为非负整数c,从源点S向该等式对应的顶点连接一条容量为c,权值为0的有向边;如果为负整数-c,从该等式对应的顶点向汇点T连接一条容量为c,权值为0的有向边。
(3)如果一个变量X[i]在第j个等式中出现为-X[i],在第k个等式中出现为+X[i],从顶点j向顶点k连接一条容量为INF,权值为V[i]的有向边。
(4)如果一个变量Y[i]在第j个等式中出现为-Y[i],在第k个等式中出现为+Y[i],从顶点j向顶点k连接一条容量为INF,权值为0的有向边。
构图以后,求从源点S到汇点T的最小费用最大流,费用值就是结果。
#include <iostream> #include <cstdio> #include <sstream> #include <cstring> #include <map> #include <cctype> #include <set> #include <vector> #include <stack> #include <queue> #include <algorithm> #include <cmath> #include <bitset> #define rap(i, a, n) for(int i=a; i<=n; i++) #define rep(i, a, n) for(int i=a; i<n; i++) #define lap(i, a, n) for(int i=n; i>=a; i--) #define lep(i, a, n) for(int i=n; i>a; i--) #define rd(a) scanf("%d", &a) #define rlld(a) scanf("%lld", &a) #define rc(a) scanf("%c", &a) #define rs(a) scanf("%s", a) #define rb(a) scanf("%lf", &a) #define rf(a) scanf("%f", &a) #define pd(a) printf("%d ", a) #define plld(a) printf("%lld ", a) #define pc(a) printf("%c ", a) #define ps(a) printf("%s ", a) #define MOD 2018 #define LL long long #define ULL unsigned long long #define Pair pair<int, int> #define mem(a, b) memset(a, b, sizeof(a)) #define _ ios_base::sync_with_stdio(0),cin.tie(0) //freopen("1.txt", "r", stdin); using namespace std; const int maxn = 1e5 + 10, INF = 0x7fffffff, LL_INF = 0x7fffffffffffffff; int n, m, s, t; int head[maxn], d[maxn], vis[maxn], nex[maxn], f[maxn], p[maxn], cnt; int xu[maxn], flow, value; struct node { int u, v, w, c; }Node[maxn]; void add_(int u, int v, int w, int c) { Node[cnt].u = u; Node[cnt].v = v; Node[cnt].w = w; Node[cnt].c = c; nex[cnt] = head[u]; head[u] = cnt++; } void add(int u, int v, int w, int c) { add_(u, v, w, c); add_(v, u, -w, 0); } int spfa() { for(int i = 0; i < maxn; i ++) d[i] = INF; deque<int> Q; mem(vis, 0); mem(p, -1); Q.push_front(s); d[s] = 0; p[s] = 0, f[s] = INF; while(!Q.empty()) { int u = Q.front(); Q.pop_front(); vis[u] = 0; for(int i = head[u];i != -1; i = nex[i]) { int v = Node[i].v; if(Node[i].c) { if(d[v] > d[u] + Node[i].w) { d[v] = d[u] + Node[i].w; p[v] = i; f[v] = min(f[u], Node[i].c); if(!vis[v]) { // cout << v << endl; if(Q.empty()) Q.push_front(v); else { if(d[v] < d[Q.front()]) Q.push_front(v); else Q.push_back(v); } vis[v] = 1; } } } } } if(p[t] == -1) return 0; flow += f[t], value += f[t] * d[t]; // cout << value << endl; for(int i = t; i != s; i = Node[p[i]].u) { Node[p[i]].c -= f[t]; Node[p[i] ^ 1].c += f[t]; } return 1; } void max_flow() { flow = value = 0; while(spfa()); pd(value); } void init() { mem(head, -1); cnt = 0; } int main() { init(); int u, v, w; rd(n), rd(m); s = 0, t = n + 3; for(int i = 1; i <= n; i++) { rd(xu[i]); } for(int i = 1; i <= m; i++) { rd(u), rd(v), rd(w); add(u, v + 1, w, INF); } for(int i = 1; i <= n+1; i++) { int tmp = xu[i] - xu[i - 1]; if(tmp > 0) add(s, i, 0, tmp); else add(i, t, 0, -tmp); if(i > 1) add(i, i - 1, 0, INF); } max_flow(); return 0; }
以上是关于志愿者招募 HYSBZ - 1061(公式建图费用流)的主要内容,如果未能解决你的问题,请参考以下文章