NOI2008 志愿者招募
Description
一个项目分\(n\)天完成,每天需要\(a_i\)个志愿者。志愿者有\(m\)种招募方式,第\(i\)种从第\(s_i\)天工作到第\(t_i\)天,工资\(c_i\)元。求完成项目的最少花费。\((n\leq 10^3,m\leq 10^4)\)
Solution
数据范围和题目描述都指向网络流,且是最小费用最大流。
但如何建图?这就不显然了。
以每天为一个节点,并建立源点和汇点。
源点连第一天,最后一天连汇点,容量为\(inf\),费用为\(0\)。
每一天连后一天,容量为\(inf-a_i\),费用为\(0\)。
对于每一类志愿者,\(s_i\)连\(t_i\),容量为\(inf\),费用为\(c_i\)。
然后跑最小费用最大流即可。
为什么这样建图可行?不难发现,只要题目有解,最大流一定为\(inf\),也就是用有费用的流将所有的\(a_i\)都填满了。
Code
#include<cstdio>
#include<queue>
#include<cstring>
#define int long long
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=10005, M=200005, inf=1ll<<60;
struct node{int to, nxt, flow, cost;}edge[M];
int head[N], vis[N], last[N], pre[N], flow[N], cost[N];
int a[N], s[M], t[M], c[M], n, m, cnt, S, T;
void prep(){memset(head, -1, sizeof(head)); cnt=-1;}
inline int read()
{
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
void add(int u, int v, int f, int w)
{
edge[++cnt]=(node){v, head[u], f, w}; head[u]=cnt;
edge[++cnt]=(node){u, head[v], 0, -w}; head[v]=cnt;
}
bool spfa()
{
rep(i, 1, n+3) cost[i]=inf; cost[S]=0;
memset(vis, 0, sizeof(vis)); vis[S]=1;
rep(i, 1, n+3) flow[i]=inf;
queue<int> q; q.push(S); pre[T]=-1;
while (!q.empty())
{
int u=q.front(); q.pop();
for (int i=head[u]; ~i; i=edge[i].nxt)
{
vis[u]=0;
int v=edge[i].to, w=edge[i].cost, f=edge[i].flow;
if (f>0 && cost[u]+w<cost[v])
{
pre[v]=u; last[v]=i; cost[v]=cost[u]+w;
flow[v]=min(flow[u], f);
if (!vis[v]) vis[v]=1; q.push(v);
}
}
}
return ~pre[T];
}
int EK()
{
int maxflow=0, mincost=0;
while (spfa())
{
maxflow+=flow[T]; mincost+=flow[T]*cost[T];
int u=T;
while (u^S)
{
edge[last[u]].flow-=flow[T];
edge[last[u]^1].flow+=flow[T];
u=pre[u];
}
}
return mincost;
}
signed main()
{
prep(); n=read(); m=read(); S=n+2; T=n+3;
rep(i, 1, n) a[i]=read();
rep(i, 1, m) s[i]=read(), t[i]=read(), c[i]=read();
add(S, 1, inf, 0); add(n+1, T, inf, 0);
rep(i, 1, n) add(i, i+1, inf-a[i], 0);
rep(i, 1, m) add(s[i], t[i]+1, inf, c[i]);
printf("%lld\n", EK());
return 0;
}