志愿者招募 [NOI2008] [鬼畜网络流]
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。
Solution
网络流的难点在于建模,此题的建模方法很鬼畜。
首先明确,这显然是一个最小费用最大流的题。
有一个源点S,汇点T,中间夹了n+1个点(对,就是n+1)
(α)首先,S->1和n+1->T分别连一条(INF,0)的边 ((x,y)表示容量x,代价y)
(β)然后对于i->i+1,连一条边为(INF-Ai,0)
(γ)在最后每一类志愿者,连一条Si->Ti+1(INF,Ci)的边
为什么要这样做?
我们一开始建的两条边(α)确保了到最后一定流量为INF (最大流特性)
然后再看剩下的边,会优先走(β),因为代价是0
但是由于减掉了Ai,意味着要保证满流必须走有代价的边(γ)
最后的答案就是(每次流量×代价和)的和了
Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #define RG register int 7 #define rep(i,a,b) for(RG i=a;i<=b;i++) 8 #define per(i,a,b) for(RG i=a;i>=b;i--) 9 #define inf (1<<30) 10 #define maxn 1005 11 #define maxm 1000005 12 using namespace std; 13 int n,m,cnt=1,S,T; 14 int ned[maxn],head[maxn],dis[maxn],vis[maxn],pre[maxn]; 15 struct E{ 16 int u,v,fl,cost,next; 17 }e[maxm<<2]; 18 inline int read() 19 { 20 int x=0,f=1;char c=getchar(); 21 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 22 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 23 return x*f; 24 } 25 26 inline void add(int u,int v,int fl,int cost) 27 { 28 e[++cnt]=(E){u,v,fl,cost,head[u]},head[u]=cnt,swap(u,v); 29 e[++cnt]=(E){u,v,0,-cost,head[u]},head[u]=cnt; 30 } 31 32 int SPFA() 33 { 34 queue<int> que; 35 memset(dis,63,sizeof(dis)),memset(pre,-1,sizeof(pre)),memset(vis,0,sizeof(vis)); 36 que.push(S),dis[S]=0; 37 RG u,v; 38 while(!que.empty()) 39 { 40 u=que.front(),que.pop();vis[u]=0; 41 for(RG i=head[u];i;i=e[i].next) 42 { 43 v=e[i].v; 44 if(e[i].fl>0&&dis[v]>dis[u]+e[i].cost) 45 { 46 pre[v]=i; 47 dis[v]=dis[u]+e[i].cost; 48 if(!vis[v]) 49 vis[v]=1,que.push(v); 50 } 51 } 52 } 53 return pre[T]!=-1; 54 } 55 56 void MCMF() 57 { 58 int ans=0; 59 while(SPFA()) 60 { 61 RG now=T,fl=inf; 62 while(now!=S) fl=min(fl,e[pre[now]].fl),now=e[pre[now]].u; 63 ans+=dis[T]*fl; 64 now=T; 65 while(now!=S) e[pre[now]].fl-=fl,e[pre[now]^1].fl+=fl,now=e[pre[now]].u; //bug 66 } 67 printf("%d",ans); 68 } 69 70 int main() 71 { 72 n=read(),m=read(),S=0,T=n+2; 73 for(RG i=1,tmp;i<=n;i++) 74 tmp=read(),add(i,i+1,inf-tmp,0); 75 for(RG i=1,u,v,val;i<=m;i++) 76 u=read(),v=read(),val=read(),add(u,v+1,inf,val); 77 add(S,1,inf,0),add(n+1,T,inf,0); 78 MCMF(); 79 return 0; 80 }