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 }

 

posted @ 2019-02-03 17:01  guapisolo  阅读(124)  评论(0编辑  收藏  举报