【洛谷P3980】志願者招募
阿新 • • 發佈:2021-07-16
題目
題目連結:https://www.luogu.com.cn/problem/P3980
申奧成功後,布布經過不懈努力,終於成為奧組委下屬公司人力資源部門的主管。布布剛上任就遇到了一個難題:為即將啟動的奧運新專案招募一批短期志願者。經過估算,這個專案需要 \(n\) 天才能完成,其中第 \(i\) 天至少需要 \(a_i\) 個人。布布通過了解得知,一共有 \(m\) 類志願者可以招募。其中第 \(i\) 類可以從第 \(s_i\) 天工作到第 \(t_i\) 天,招募費用是每人 \(c_i\) 元。新官上任三把火,為了出色地完成自己的工作,布布希望用盡量少的費用招募足夠的志願者,但這並不是他的特長!於是布布找到了你,希望你幫他設計一種最優的招募方案。
\(n\leq 1000,m\leq 10^4\)
思路
如果是一般的建圖方法,無法處理這種一個點對一段區間的費用流。所以需要改變建圖的思路。
建立 \(n+1\) 個點,點 \(i\) 向點 \(i+1\) 連一條流量為 \(+\infty-a_i\),費用為 \(0\) 的邊,表示限制第 \(i\) 天至少要有 \(a_i\) 名志願者。
然後對於一類志願者 \(j\),從 \(s_j\) 向 \(t_{j}+1\) 連一條流量為 \(+\infty\),費用為 \(c_j\) 的邊,如果這條邊流過了 \(k\) 的流量,表示這一類志願者選 \(k\) 名。
然後連邊 \((S,1,+\infty,0),(n+1,T,+\infty,0)\)
這樣的話,因為有流量的限制,當 \(+\infty\) 的流量從源點流向 \(1\) 時,至少要有 \(a_1\) 的流量從志願者的邊流走,因為從 \(1\) 到 \(2\) 的邊的流量只有 \(\infty-a_1\)。後面的點同理。
然後跑最小費用最大流即可。
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1010,M=30010; const ll Inf=1e18; int n,m,S,T,tot=1,head[N],pre[N]; ll cost,dis[N]; bool vis[N]; struct edge { int next,to; ll flow,cost; }e[M]; void add(int from,int to,ll flow,ll cost) { e[++tot]=(edge){head[from],to,flow,cost}; head[from]=tot; swap(from,to); e[++tot]=(edge){head[from],to,0,-cost}; head[from]=tot; } bool spfa() { memset(dis,0x3f3f3f3f,sizeof(dis)); queue<int> q; q.push(S); dis[S]=0; while (q.size()) { int u=q.front(); q.pop(); vis[u]=0; for (int i=head[u];~i;i=e[i].next) { int v=e[i].to; if (e[i].flow && dis[v]>dis[u]+e[i].cost) { dis[v]=dis[u]+e[i].cost; pre[v]=i; if (!vis[v]) vis[v]=1,q.push(v); } } } return dis[T]<Inf; } void addflow() { ll minf=Inf; for (int i=T;i!=S;i=e[pre[i]^1].to) minf=min(minf,e[pre[i]].flow); for (int i=T;i!=S;i=e[pre[i]^1].to) e[pre[i]].flow-=minf,e[pre[i]^1].flow+=minf; cost+=minf*dis[T]; } void MCMF() { while (spfa()) addflow(); } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); S=N-1; T=N-2; add(S,1,Inf,0); add(n+1,T,Inf,0); for (int i=1,x;i<=n;i++) { scanf("%d",&x); add(i,i+1,Inf-x,0); } for (int i=1,x,y,z;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y+1,Inf,z); } MCMF(); cout<<cost; return 0; }