有m个项目和n个员工,做项目i可以获得Ai元,但是必须雇用若干指定的员工。雇用员工j需要Bj元,一旦雇用便可以参与多个项目。问最大收益。
1<=M,N<=100.
最小割。
源点向每个项目点连一条容量为项目获利的边。
每个项目点向本项目需要的所有员工点连一条容量为正无穷的边。
每个员工点向汇点连一条容量为雇用所需费用的边。
答案为所有项目获利和减最小割。
“蕴含式最大获利问题”,套用最大权闭合子图的建模即可。
割掉源点向项目点的边即为放弃此项目。
割掉员工点向汇点的边即为雇用此员工。
项目点和员工点间的边不可割去。
1 #include<cstring> 2 #include<cstdio> 3 #include<algorithm> 4 #include<iostream> 5 #include<queue> 6 using namespace std; 7 const int dian=210; 8 const int bian=30005; 9 const int INF=0x3f3f3f3f; 10 int ch[dian],h[dian],nxt[bian],ver[bian],val[bian],cr[dian]; 11 int n,m,ans,aa,bb,tot; 12 int S,T; 13 void add(int a,int b,int c){ 14 tot++;ver[tot]=b;nxt[tot]=h[a];val[tot]=c;h[a]=tot; 15 tot++;ver[tot]=a;nxt[tot]=h[b];val[tot]=0;h[b]=tot; 16 } 17 bool tell(){ 18 memset(ch,-1,sizeof(ch)); 19 queue<int>q; 20 q.push(S); 21 ch[S]=0; 22 while(!q.empty()){ 23 int t=q.front(); 24 q.pop(); 25 for(int i=h[t];i;i=nxt[i]) 26 if(ch[ver[i]]==-1&&val[i]){ 27 q.push(ver[i]); 28 ch[ver[i]]=ch[t]+1; 29 } 30 } 31 return ch[T]!=-1; 32 } 33 int zeng(int a,int b){ 34 if(a==T) 35 return b; 36 int r=0; 37 for(int i=cr[a];i&&b>r;i=nxt[i]) 38 if(ch[ver[i]]==ch[a]+1&&val[i]){ 39 int t=zeng(ver[i],min(b-r,val[i])); 40 val[i]-=t,r+=t,val[i^1]+=t; 41 if(val[i]) 42 cr[a]=i; 43 } 44 if(!r) 45 ch[a]=-1; 46 return r; 47 } 48 int dinic(){ 49 int r=0,t; 50 while(tell()){ 51 for(int i=1;i<=n+m+2;i++) 52 cr[i]=h[i]; 53 while(t=zeng(S,INF)) 54 r+=t; 55 } 56 return r; 57 } 58 int main(){ 59 int cas; 60 scanf("%d",&cas); 61 while(cas--){ 62 memset(nxt,0,sizeof(nxt)); 63 memset(h,0,sizeof(h)); 64 ans=0; 65 tot=1; 66 scanf("%d%d",&n,&m); 67 S=n+m+1; 68 T=n+m+2; 69 for(int i=1;i<=n;i++){ 70 scanf("%d",&aa); 71 ans+=aa; 72 add(S,i,aa); 73 } 74 for(int i=1;i<=m;i++){ 75 scanf("%d",&aa); 76 add(i+n,T,aa); 77 } 78 for(int i=1;i<=n;i++){ 79 scanf("%d",&aa); 80 for(int j=1;j<=aa;j++){ 81 scanf("%d",&bb); 82 add(i,bb+1+n,INF); 83 } 84 } 85 printf("%d\n",ans-dinic()); 86 } 87 return 0; 88 }