[NOI2008]志愿者招募
转载自byvoid
这道题正确的解法是构造网络,求网络最小费用最大流,但是模型隐藏得较深,不易想到。
构造网络是该题的关键,以下面一个例子说明构图的方法和解释。
例如一共需要 4 天,四天需要的人数依次是 4, 2, 5, 3
。有 5 类志愿者,如下表所示:
种类 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
时间 | 1-2 | 1-1 | 2-3 | 3-3 | 3-4 |
费用 | 3 | 4 | 3 | 5 | 6 |
设雇佣第
对于第
在上述四个等式上下添加
观察发现,每个变量都在两个式子中出现了,而且一次为正,一次为负。所有等式右边和为
接下来,根据上面五个等式构图。
每个等式为图中一个顶点,添加源点
如果一个等式右边为非负整数
如果一个变量
如果一个变量
构图以后,求从源点
根据上面的例子可以构造出如下网络,红色的边为每个变量
在这个图中求最小费用最大流,流量网络如下图,每个红色边的流量就是对应的变量
所以,答案为
上面的方法很神奇地求出了结果,思考为什么这样构图。我们将最后的五个等式进一步变形,得出以下结果
可以发现,每个等式左边都是几个变量和一个常数相加减,右边都为
每个正的变量相当于流入该顶点的流量,负的变量相当于流出该顶点的流量,而正常数可以看作来自附加源点的流量,负的常数是流向附加汇点的流量。
因此可以据此构造网络,求出从附加源到附加汇的网络最大流,即可满足所有等式。而我们还要求
然而在 NOI
的现场上,该题得分的平均分
不能不说这是一道难题,难就难在抽象出问题的数学模型,设计有效的算法。而信息学竞赛正朝着这个方向发展,数学建模将是解决问题的共同关键步骤。
Problem
Description
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。
布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。
经过估算,这个项目需要
布布通过了解得知,一共有
新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!
于是布布找到了你,希望你帮他设计一种最优的招募方案。
Input
第一行包含两个整数
接下来的一行中包含
接下来的
为了方便起见,我们可以认为每类志愿者的数量都是无限多的。
Output
仅包含一个整数,表示你所设计的最优方案的总费用。
Sample Input
3 32 3 41 2 22 3 53 3 2
Sample Output
14
Hint
Code
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#define inf 0x3F3F3F3Fusing namespace std;struct Node{ int to,next,v,c;}e[100005];int S,T,ans,a[1005],h[1005],d[1005],pre[1005],cnt=1;bool vis[1005];inline void Addedge(int x,int y,int v,int c){ e[++cnt]=(Node){y,h[x],v,c};h[x]=cnt; e[++cnt]=(Node){x,h[y],0,-c};h[y]=cnt; return;}inline bool SPFA(){ int i,x,y; queue<int>q;q.push(S); memset(d,0x3F,sizeof(d));d[S]=0; while(!q.empty()) { x=q.front();q.pop(); vis[x]=false; for(i=h[x];i;i=e[i].next) { y=e[i].to; if(e[i].v&&d[y]>d[x]+e[i].c) { d[y]=d[x]+e[i].c; pre[y]=i; if(!vis[y]){q.push(y);vis[y]=true;} } } } return d[T]<inf;}inline void Adjust(){ int i,j=T,delta=inf; while(pre[j]) { i=pre[j]; if(e[i].v<delta)delta=e[i].v; j=e[i^1].to; } ans+=delta*d[T];j=T; while(pre[j]) { i=pre[j]; e[i].v-=delta; e[i^1].v+=delta; j=e[i^1].to; } return;}int main(void){ int i,x,y,v,n,m; scanf("%d%d",&n,&m); for(i=1;i<=n;++i)scanf("%d",&a[i]); for(i=1;i<=m;++i) { scanf("%d%d%d",&x,&y,&v); Addedge(x,y+1,inf,v); } S=n+2;T=n+3; for(i=1;i<=n+1;++i) { v=a[i]-a[i-1]; if(v>=0)Addedge(S,i,v,0); else Addedge(i,T,-v,0); } for(i=1;i<=n;++i)Addedge(i+1,i,inf,0); while(SPFA())Adjust(); printf("%d\n",ans); return 0;}