BZOJ 1061: [Noi2008]志愿者招募
1061: [Noi2008]志愿者招募
Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 4064 Solved: 2476
[Submit][Status][Discuss]
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
2 3 4
1 2 2
2 3 5
3 3 2
Sample Output
14
HINT
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。
Source
分析:
%LYD...
这道题普遍做法貌似都是线性规划单纯形的做法,LYD告诉我们可以跑上下界最小费用可行流...
我们把每一天看成一个点,然后从i向i+1连边,上界为inf,下界为ai,费用为0...
然后对于每一类志愿者,从ti+1到si连边,上界为inf,下界为0,费用为ci...这样每花费ci的代价,就从si到ti增加一个流...
然后就转化成了无源汇上下界最小费用可行流,其实就是把无源汇上下界可行流的最大流转化成最小费用最大流...
怎么求无源汇上下界可行流?
如果把C-B作为容量上界,0作为容量下界,就是一般的网络流模型。
然而求出的实际流量为f(u,v)+B(u,v),不一定满足流量守恒,需要调整。
设inB[u]=∑B(i,u),outB[u]=∑B(u,i),d[u]=inB[u]-outB[u]。
新建源汇,S向d>0的点连边,d<0的点向汇点连边,容量为相应的d。 在该网络上求最大流,则每条边的流量+下界就是原网络的一个可行流。
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<queue> 6 //by NeighThorn 7 #define inf 0x3f3f3f3f 8 #define INF 0x3f3f3f3f3f3f 9 using namespace std; 10 11 const int maxn=1000+5,maxm=100000+5; 12 13 int n,m,S,T,cnt,w[maxm],hd[maxn],fl[maxm],to[maxm],nxt[maxm],Min[maxn],vis[maxn],from[maxn]; 14 15 long long dis[maxn],dif[maxn]; 16 17 inline bool spfa(void){ 18 for(int i=S;i<=T;i++) 19 dis[i]=INF,Min[i]=inf; 20 queue<int> q;q.push(S),vis[S]=1,dis[S]=0; 21 while(!q.empty()){ 22 int top=q.front();q.pop();vis[top]=0; 23 for(int i=hd[top];i!=-1;i=nxt[i]) 24 if(dis[to[i]]>dis[top]+w[i]&&fl[i]){ 25 from[to[i]]=i; 26 dis[to[i]]=dis[top]+w[i]; 27 Min[to[i]]=min(Min[top],fl[i]); 28 if(!vis[to[i]]) 29 vis[to[i]]=1,q.push(to[i]); 30 } 31 } 32 return dis[T]!=INF; 33 } 34 35 inline long long find(void){ 36 for(int i=T;i!=S;i=to[from[i]^1]) 37 fl[from[i]]-=Min[T],fl[from[i]^1]+=Min[T]; 38 return dis[T]*Min[T]; 39 } 40 41 inline int dinic(void){ 42 int res=0; 43 while(spfa()) 44 res+=find(); 45 return res; 46 } 47 48 inline void add(int l,int s,int x,int y){ 49 w[cnt]=l;fl[cnt]=s;to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++; 50 w[cnt]=-l;fl[cnt]=0;to[cnt]=x;nxt[cnt]=hd[y];hd[y]=cnt++; 51 } 52 53 signed main(void){ 54 // freopen("in.txt","r",stdin); 55 memset(hd,-1,sizeof(hd)); 56 scanf("%d%d",&n,&m);S=0,T=n+2; 57 for(int i=1,y;i<=n;i++) 58 scanf("%d",&y),add(0,inf,i,i+1),dif[i]-=y,dif[i+1]+=y; 59 for(int i=1,s,x,y;i<=m;i++) 60 scanf("%d%d%d",&x,&y,&s),add(s,inf,y+1,x); 61 for(int i=1;i<=n+1;i++){ 62 if(dif[i]>0) 63 add(0,dif[i],S,i); 64 else if(dif[i]<0) 65 add(0,-dif[i],i,T); 66 } 67 printf("%d\n",dinic()); 68 return 0; 69 }
By NeighThorn