题面: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 }
来源:https://www.cnblogs.com/guapisolo/p/10350672.html