题目大意:给定n个人,每个人有一个佣金,i和j如果同时被雇佣会产生2*E(i,j)的效益,i和j如果一个被雇佣一个不被雇佣会产生E(i,j)的亏损,求最大收益
首先对于每一个cost[i],从点i出发向汇点连一条流量为cost[i]的边
对于每一对点(i,j),建图如下:
从S向点i和点j各连一条流量为E(i,j)的边
i和j之间连一条流量为2*E(i,j)的双向边
这样可以保证每种割法对应一种雇佣方案
用矩阵上数字的总和减掉最小割即是答案
边集会很大,因此合并后再加即可
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 1010 #define S 0 #define T (n+1) #define INF 0x3f3f3f3f3f3f3f3fll using namespace std; struct abcd{ int to,next; long long f; }table[10010010]; int head[M],tot=1; int n; long long ans,a[M]; void Add(int x,int y,long long z) { table[++tot].to=y; table[tot].f=z; table[tot].next=head[x]; head[x]=tot; } void Link(int x,int y,int z) { Add(x,y,z); Add(y,x,z); } namespace Max_Flow{ int dpt[M]; bool BFS() { static int q[M]; int i,r=0,h=0; memset(dpt,-1,sizeof dpt); q[++r]=S;dpt[S]=1; while(r!=h) { int x=q[++h]; for(i=head[x];i;i=table[i].next) if(table[i].f&&!~dpt[table[i].to]) { dpt[table[i].to]=dpt[x]+1; q[++r]=table[i].to; if(table[i].to==T) return true; } } return false; } long long Dinic(int x,long long flow) { int i;long long left=flow; if(x==T) return flow; for(i=head[x];i&&left;i=table[i].next) if(table[i].f&&dpt[table[i].to]==dpt[x]+1) { long long temp=Dinic(table[i].to,min(left,table[i].f) ); left-=temp; table[i].f-=temp; table[i^1].f+=temp; } if(left) dpt[x]=-1; return flow-left; } } int main() { int i,j,x; cin>>n; for(i=1;i<=n;i++) { scanf("%d",&x); Link(i,T,x); } for(i=1;i<=n;i++) for(j=1;j<=n;j++) { scanf("%d",&x);ans+=x; if(i>=j) continue; a[i]+=x;a[j]+=x; Link(i,j,(long long)x<<1); } for(i=1;i<=n;i++) Link(S,i,a[i]); using namespace Max_Flow; while( BFS() ) ans-=Dinic(S,INF); cout<<ans<<endl; return 0; }
BZOJ 2039 2009国家集训队 employ人员雇佣 最小割
原文地址:http://blog.csdn.net/popoqqq/article/details/42609421