BZOJ 2039 人员雇佣 (最小割)
题面:BZOJ传送门
网络流的题真神仙= =
大致分为三种情况
- 选某个人$i$,收益减少$a_{i}$
- 选了$i$选了$j$,收益增加$2e_{ij}$
- 选了$i$不选$j$,收益减少$e_{ij}$
收益问题用最小割的常用套路,实际收益$=$可能的收益总和$sum-$最小割
考虑最小割如何建图
源点$S$向$i$连流量为$\sum_{j}e_{ij}$的边,$i,j$之间连流量为$2e_{ij}$的双向边,$i$向汇点$T$连流量为$a_{i}$的边
对于两个人$i$和$j$,把上述三种情况带入
选了i选了j
$i,j$和$T$之间的边被割断,流量为$a_{i}$和$a_{j}$,减掉即可,$2e_{ij}$这部分收益已经在$sum$里了,不需要管
选了i不选j
$i$和$T$之间的边被割断,$S$和$j$之间的边被割断。
此时,连接在$i,j$之间流量为$2e_{ij}$的边就要发挥作用了
如果想继续保持现在的割边状态,会有一条流量为$2e_{ij}$的流$S$->$i$->$j$->$T$
其中$e_{ji}$($j$对这种情况的贡献)已经在割$S->j$时从$sum$里去掉了
我们不仅要去掉$sum$里的$e_{ij}$($i$的贡献)。还要去掉一份e_{ij},因为题目要求选$i$不选$j$会带来额外的负收益$e_{ij}$,一共是$2e_{ij}$
所以$i,j$之间的边流量是$2e_{ij}$
不选i不选j
收益减少$2e_{ij}$,分别在割掉$S$和$i$的边以及$S$和$j$的边被去掉了
这道题目启示我们,化边为点能解决很多问题,但较为暴力
在不知道如何维护“割”这样一个结构时,可以考虑问题的本质,对某些点的某些量进行求和
另外这题爆int
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define N1 1010 6 #define M1 1010000 7 #define ll long long 8 using namespace std; 9 //re 10 11 const ll inf=0x3f3f3f3f3f3f; 12 int gint() 13 { 14 int ret=0,fh=1; char c=getchar(); 15 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 16 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 17 return ret*fh; 18 } 19 struct Edge{ 20 int head[N1],to[M1<<1],nxt[M1<<1],cte; ll flow[M1<<1]; 21 void ae(int u,int v,ll f) 22 { 23 cte++; to[cte]=v; nxt[cte]=head[u]; 24 head[u]=cte; flow[cte]=f; 25 } 26 }e; 27 28 int n,m,hd,tl,S,T; 29 int dep[N1],cur[N1],que[M1]; 30 int bfs() 31 { 32 int x,j,v; 33 memset(dep,-1,(T+1)<<2); memcpy(cur,e.head,(T+1)<<2); 34 hd=1,tl=0; que[++tl]=S; dep[S]=0; 35 while(hd<=tl) 36 { 37 x=que[hd++]; 38 for(j=e.head[x];j;j=e.nxt[j]) 39 { 40 v=e.to[j]; 41 if( e.flow[j]>0 && dep[v]==-1 ) 42 dep[v]=dep[x]+1, que[++tl]=v; 43 } 44 } 45 return dep[T]!=-1; 46 } 47 ll dfs(int x,ll limit) 48 { 49 int j,v;ll flow,ans=0; 50 if(x==T||!limit) return limit; 51 for(j=cur[x];j;j=e.nxt[j]) 52 { 53 v=e.to[j]; cur[x]=j; 54 if( dep[v]==dep[x]+1 && (flow=dfs(v,min(e.flow[j],limit))) ) 55 { 56 e.flow[j]-=flow; limit-=flow; 57 e.flow[j^1]+=flow; ans+=flow; 58 if(!limit) break; 59 } 60 } 61 return ans; 62 } 63 ll Dinic() 64 { 65 ll ans=0; 66 while(bfs()) 67 ans+=dfs(S,inf); 68 return ans; 69 } 70 71 72 int a[N1],E[N1][N1]; ll sum[N1]; 73 int main() 74 { 75 scanf("%d",&n); S=0; T=n+1; e.cte=1; 76 int i,j,s; ll k,tot=0,ans=0; 77 for(i=1;i<=n;i++) a[i]=gint(); 78 for(i=1;i<=n;i++) for(j=1;j<=n;j++) 79 E[i][j]=gint(), sum[i]+=E[i][j], tot+=E[i][j]; 80 for(i=1;i<=n;i++) for(j=1;j<=n;j++) 81 { 82 if(i==j) continue; 83 e.ae(i,j,2*E[i][j]); e.ae(j,i,0); 84 } 85 for(i=1;i<=n;i++) e.ae(S,i,sum[i]), e.ae(i,S,0); 86 for(i=1;i<=n;i++) e.ae(i,T,a[i]), e.ae(T,i,a[i]); 87 ans=Dinic(); 88 printf("%lld\n",tot-ans); 89 return 0; 90 }