【洛谷P3980】志愿者招募
题目
题目链接: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;
}