誌願者招募 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\n", a) #define plld(a) printf("%lld\n", a) #define pc(a) printf("%c\n", a) #define ps(a) printf("%s\n", 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(公式建圖費用流)