[Noi2008]志愿者招募
Time Limit:20000MS Memory Limit:165536K
Total Submit:262 Accepted:162
Case Time Limit:2000MS
Description
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。
布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最 优的招募方案。
Input
第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。
接下来的一行中包含N 个非负整数,表示每天至少需要的志愿者人数。
接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了方便起见,我们可以认为每类志愿者的数量都是无限多的。
Output
仅包含一个整数,表示你所设计的最优方案的总费用。
Sample Input
3 3
2 3 4
1 2 2
2 3 5
3 3 2
Sample Output
14
Hint
招募第一类志愿者3名,第三类志愿者4名
30%的数据中,1 ≤ N, M ≤ 10,1 ≤ Ai ≤ 10;
100%的数据中,1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均
不超过2^31-1。
这两天做题有点杂,POJ、SGU、APIO都是看到自己什么地方弱做什么……
此题一看就是费用流……明显的,可以把每一天看成是一个节点,然后跑最小费用最大流……但是构图挺难的。
首先需要明确一个概念:每一天的开始和结束,他们不是一个点。第一天的结束等价于第二天的开始,以此类推……所以,n天一共就是n+1个点。
然后我们设立source和sink。对于一天的开始(就是1..n),与源连边,流量为当天所需人数(a[i]),费用为0;对于每一天的结束(就是第二天的开始,2..n+1),与汇连一条边,流量为当天所需人数(注意是a[i-1]!),费用为0。这样我们可以满足每一天的人数需求。
对于每个种类的志愿者,将起始日(s)的开始于结束日的结束(t+1)连一条边,流量为INF(因为志愿者数量是无限的),费用为招募费用。
然后对于每一天的结束,与这一天的开始连一条容量为INF,费用为0的边,这样可以满足招募志愿者花费的费用只统计一次。
网络模型建好之后,就可以直接跑费用流了……
Clavichord非说SPFA会TLE……然后我就很淡定地写SPFA……AC了……
program NOI_2008_Employee; type rec=record s,endv,next,w,c,pre:longint; end; var edge:array[1..50000]of rec; pre,w,father:Array[0..10002]of longint; q:array[1..1000000]of longint; vis:array[0..10002]of boolean; dist:Array[0..10002]of longint; n,m,source,sink,ans,total:longint; procedure addedge(s,t,w,c:longint); begin inc(total); edge[total].s:=s; edge[total].endv:=t; edge[total].w:=w; edge[total].c:=c; edge[total].next:=father[s]; father[s]:=total; edge[total].pre:=total+1; inc(total); edge[total].s:=t; edge[total].endv:=s; edge[total].w:=0; edge[total].c:=-c; edge[total].next:=father[t]; father[t]:=total; edge[total].pre:=total-1; end; function cmp(a,b:longint):longint; begin if a<b then exit(a); exit(b); end; procedure init; var i,a,b,c:longint; begin fillchar(father,sizeof(father),$ff); readln(n,m); source:=0;sink:=n+2; for i:=1 to n do read(w[i]); for i:=1 to n do addedge(source,i,w[i],0); for i:=2 to n+1 do addedge(i,sink,w[i-1],0); for i:=1 to m do begin readln(a,b,c); addedge(a,b+1,maxlongint,c); end; for i:=2 to n+1 do addedge(i,i-1,maxlongint,0); end; function spfa:boolean; var head,tail,now,p:longint; begin fillchar(vis,sizeof(vis),false); fillchar(pre,sizeof(pre),0); head:=1;tail:=1; q[head]:=0; fillchar(dist,sizeof(dist),$7f); dist[0]:=0; vis[0]:=true; while head<=tail do begin now:=q[head]; p:=father[now]; while p>0 do begin if (edge[p].w>0)and(dist[edge[p].endv]>dist[now]+edge[p].c)then begin dist[edge[p].endv]:=dist[now]+edge[p].c; pre[edge[p].endv]:=p; if not vis[edge[p].endv] then begin vis[edge[p].endv]:=true; inc(tail); q[tail]:=edge[p].endv; end; end; p:=edge[p].next; end; vis[now]:=false; inc(head); end; exit(dist[sink]<1000000); end; procedure augument; var min,p:longint; begin while spfa do begin p:=pre[sink]; min:=maxlongint; while edge[p].s<>source do begin min:=cmp(edge[p].w,min); p:=pre[edge[p].s]; end; min:=cmp(edge[p].w,min); p:=pre[sink]; while edge[p].s<>source do begin dec(edge[p].w,min); inc(edge[edge[p].pre].w,min); p:=pre[edge[p].s]; end; dec(edge[p].w,min); inc(ans,dist[sink]*min); end end; begin assign(input,'employee.in');reset(input); assign(output,'employee.out');rewrite(output); init; augument; writeln(ans); readln;readln; close(output); end.