bzoj1061 NOI2018 志愿者招募——solution
Description
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难
题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要
Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用
是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这
并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。
Input
第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含N 个非负
整数,表示每天至少需要的志愿者人数。 接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了
方便起见,我们可以认为每类志愿者的数量都是无限多的。
Output
仅包含一个整数,表示你所设计的最优方案的总费用。
-bybzoj
http://www.lydsy.com/JudgeOnline/problem.php?id=1061
介绍一种简单的有上下界费用流解法:
把每个志愿者看做1流量,
首先把n天按从1到n的顺序顺次连INF的边,任何刑期未满的志愿者都可以在这个边上流动;
由于每天有一个人数下限,于是考虑,拆点,在代表同一天的两点见连一条有下界的边,
如何把志愿者引入这个图?
考虑每个志愿者依照自己所属的种类,将在固定的点进入图,固定的点离开图,并在这两点间流动,
于是若类型i的志愿者从ai入,bi出,
则若只有i的话,ai有多少流量被引入,bi就有多少流量消失,不妨连一条费用1流量INF的边从bi到ai
至此网络流图模型便建好了
至此网络流图模型便建好了
在此图上跑有上下界的最小费用流即可;
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int INF=0x3f3f3f3f; int n,m,S,T,ansf,answ; struct ss{ int next,to,f,v,cp; }e[28010]; int first[2010],num; int que[100010],vis[2010],flow[2010],way[2010],dis[2010],pre[2010]; void bui_(int ,int ,int ,int ); void build(int ,int ,int ,int ); bool spfa(); void EK(); int main(){ int i,j,k,l; scanf("%d%d",&n,&m); S=(n<<1)+1,T=S+1; for(i=1;i<=n;i++){ bui_(i,i+n,INF,0); scanf("%d",&j); if(j)bui_(S,i+n,j,0),bui_(i,T,j,0); if(i!=n) bui_(i+n,i+1,INF,0); } for(i=1;i<=m;i++){ scanf("%d%d%d",&j,&k,&l); bui_(k+n,j,INF,l); } while(spfa()) EK(); printf("%d\n",ansf); } void bui_(int f,int t,int fi,int vi){ build(f,t,fi,vi),e[num].cp=num+1; build(t,f,0,-vi),e[num].cp=num-1; } void build(int f,int t,int fi,int vi){ e[++num].next=first[f]; e[num].to=t,e[num].f=fi,e[num].v=vi; first[f]=num; } bool spfa(){ int i,h=0,t=1; memset(dis,0x3f,sizeof(dis)); que[t]=S,vis[S]=1,dis[S]=0,flow[S]=INF; while(h<t){ vis[que[++h]]=0; for(i=first[que[h]];i;i=e[i].next) if(e[i].f&&dis[e[i].to]>dis[que[h]]+e[i].v){ dis[e[i].to]=dis[que[h]]+e[i].v; way[e[i].to]=i,pre[e[i].to]=que[h]; flow[e[i].to]=min(flow[que[h]],e[i].f); if(!vis[e[i].to]){ que[++t]=e[i].to; vis[que[t]]=1; } } } return dis[T]!=INF; } void EK(){ int i; ansf+=flow[T]*dis[T]; answ+=flow[T]; for(i=T;i;i=pre[i]) if(way[i]) e[way[i]].f-=flow[T],e[e[way[i]].cp].f+=flow[T]; }
Just close your eyes, you`ll be alright, no one can hurt you after you die.