题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1061
题意:申奥成功后,布布经过不懈努力,终于 成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最 优的招募方案。
思路:
例如一共需要4天,四天需要的人数依次是4,2,5,3。有5类志愿者,如下表所示:
设雇佣第i类志愿者的人数为X[i],每个志愿者的费用为V[i],第j天雇佣的人数为P[j],则每天的雇佣人数应满足一个不等式,如上表所述,可以列出
P[1]=X[1]+X[2]>=4
P[2]=X[1]+X[3]>=2
P[3]=X[3]+X[4]+X[5]>=5
P[4]=X[5]>=3
对于第i个不等式,添加辅助变量Y[i](Y[i]>=0),可以使其变为等式
P[1]=X[1]+X[2]-Y[1]=4
P[2]=X[1]+X[3]-Y[2]=2
P[3]=X[3]+X[4]+X[5]-Y[3]=5
P[4]=X[5]-Y[4]=3
在上述四个等式上下添加P[0]=0,P[5]=0,每次用下边的式子减去上边的式子,得出
① P[1]-P[0]=X[1]+X[2]-Y[1]=4
② P[2]-P[1]=X[3]-X[2]-Y[2]+Y[1]=-2
③ P[3]-P[2]=X[4]+X[5]-X[1]-Y[3]+Y[2]=3
④ P[4]-P[3]=-X[3]-X[4]+Y[3]-Y[4]=-2
⑤ P[5]-P[4]=-X[5]+Y[4]=-3
观察发现,每个变量都在两个式子中出现了,而且一次为正,一次为负.所有等式右边和为0.我们将最后的五个等式进一步变形,得出以下结果
① -X[1]-X[2]+Y[1]+4=0
② -X[3]+X[2]+Y[2]-Y[1]-2=0
③ -X[4]-X[5]+X[1]+Y[3]-Y[2]+3=0
④ X[3]+X[4]-Y[3]+Y[4]-2=0
⑤ X[5]-Y[4]-3=0
可 以发现,每个等式左边都是几个变量和一个常数相加减,右边都为0,恰好就像网络流中除了源点和汇点的顶点都满足流量平衡。每个正的变量相当于流入该顶点的 流量,负的变量相当于流出该顶点的流量,而正常数可以看作来自附加源点的流量,负的常数是流向附加汇点的流量。因此可以据此构造网络,求出从附加源到附加 汇的网络最大流,即可满足所有等式。而我们还要求费用最小,所以要在X变量相对应的边上加上权值,然后求最小费用最大流。
接下来,根据上面五个等式构图。
(1)每个等式为图中一个顶点,添加源点S和汇点T。
(2)如果一个等式中的数字为非负整数c,从源点S向该等式对应的顶点连接一条容量为c,权值为0的有向边;如果为负整数-c,从该等式对应的顶点向汇点T连接一条容量为c,权值为0的有向边。
(3)如果一个变量X[i]在第j个等式中出现为-X[i],在第k个等式中出现为+X[i],从顶点j向顶点k连接一条容量为INF,权值为V[i]的有向边。
(4)如果一个变量Y[i]在第j个等式中出现为-Y[i],在第k个等式中出现为+Y[i],从顶点j向顶点k连接一条容量为INF,权值为0的有向边。
构图以后,求从源点S到汇点T的最小费用最大流,费用值就是结果。
struct node { int cost,flow,v,u,next; }; node edges[N*1000]; int head[N],e,s,t; int pre[N]; void add(int u,int v,int flow,int cost) { edges[e].u=u; edges[e].v=v; edges[e].cost=cost; edges[e].flow=flow; edges[e].next=head[u]; head[u]=e++; } void Add(int u,int v,int flow,int cost) { add(u,v,flow,cost); add(v,u,0,-cost); } int SPFA(int s,int t) { clr(pre,-1); int F[N],h[N],C[N],i,u,v,f,c; queue<int> Q; FOR0(i,t+1) C[i]=INF,h[i]=0,F[i]=0; Q.push(s); C[s]=0; F[s]=INF; while(!Q.empty()) { u=Q.front(); Q.pop(); h[u]=0; for(i=head[u];i!=-1;i=edges[i].next) { v=edges[i].v; f=edges[i].flow; c=edges[i].cost; if(f>0&&C[v]>C[u]+c) { C[v]=C[u]+c; F[v]=min(F[u],f); pre[v]=i; if(!h[v]) h[v]=1,Q.push(v); } } } return F[t]; } int MCMF(int s,int t) { int ans=0,i,temp; while(temp=SPFA(s,t)) { for(i=pre[t];i!=-1;i=pre[edges[i].u]) { ans+=temp*edges[i].cost; edges[i].flow-=temp; edges[i^1].flow+=temp; } } return ans; } int n,m,d[N]; int main() { RD(n,m); clr(head,-1); e=0; s=0; t=n+2; int i,u,v,c,temp; FOR1(i,n) RD(d[i]); FOR1(i,m) { RD(u,v,c); Add(u,v+1,INF,c); } FOR1(i,n+1) { temp=d[i]-d[i-1]; if(temp>=0) Add(s,i,temp,0); else Add(i,t,-temp,0); if(i>1) Add(i,i-1,INF,0); } PR(MCMF(s,t)); }