[bzoj1061]志愿者招募
Posted pywbktda
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj1061]志愿者招募相关的知识,希望对你有一定的参考价值。
将问题反过来考虑,即最多选择多少人(流量最大)使得答案最少(费用最少),很明显是一个费用流的模型
用一条流表示每一天的人数都+1,可以发现即按如下方式建图:1.对于每一种志愿者,li向ri+1连(+oo,ci)的边;2.对于每一天,向前一天连(+oo,0)的边,表示可以多次覆盖;3.源点向1连一条max(ai)的边
但这样会导致答案过大,因为实际上并不需要每一天都被覆盖max(ai)次
仍然反过来考虑,可以理解为我可以不被覆盖max(ai)-ai次,因此i向i+1连一条max(ai)-ai的边即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1005 4 #define ll long long 5 struct ji{ 6 int nex,to,cost; 7 ll len; 8 }edge[N*30]; 9 queue<int>q; 10 int E,n,m,x,y,z,head[N],a[N],vis[N],from[N]; 11 ll d[N]; 12 void add(int x,int y,ll z,int w){ 13 edge[E].nex=head[x]; 14 edge[E].to=y; 15 edge[E].len=z; 16 edge[E].cost=w; 17 head[x]=E++; 18 if (E&1)add(y,x,0,-w); 19 } 20 bool spfa(){ 21 memset(d,0x3f,sizeof(d)); 22 memset(vis,0,sizeof(vis)); 23 q.push(0); 24 d[0]=0; 25 while (!q.empty()){ 26 int k=q.front(); 27 q.pop(); 28 vis[k]=0; 29 for(int i=head[k];i!=-1;i=edge[i].nex){ 30 int v=edge[i].to; 31 if ((edge[i].len)&&(d[v]>d[k]+edge[i].cost)){ 32 d[v]=d[k]+edge[i].cost; 33 from[v]=i; 34 if (!vis[v]){ 35 vis[v]=1; 36 q.push(v); 37 } 38 } 39 } 40 } 41 return d[n+1]!=d[n+2]; 42 } 43 int main(){ 44 scanf("%d%d",&n,&m); 45 ll s=0,ans=0; 46 memset(head,-1,sizeof(head)); 47 for(int i=1;i<=n;i++){ 48 scanf("%d",&a[i]); 49 s+=a[i]; 50 } 51 add(0,1,s,0); 52 for(int i=1;i<=n;i++)add(i,i+1,s-a[i],0); 53 for(int i=1;i<=m;i++){ 54 scanf("%d%d%d",&x,&y,&z); 55 add(x,y+1,s,z); 56 } 57 while (spfa()){ 58 s=1e18; 59 for(int i=n+1;i;i=edge[from[i]^1].to)s=min(s,edge[from[i]].len); 60 ans+=s*d[n+1]; 61 for(int i=n+1;i;i=edge[from[i]^1].to){ 62 edge[from[i]].len-=s; 63 edge[from[i]^1].len+=s; 64 } 65 } 66 printf("%lld",ans); 67 }
以上是关于[bzoj1061]志愿者招募的主要内容,如果未能解决你的问题,请参考以下文章