传送门
最小割经典题目。
题目让我们求最大的贡献。
那我们先考虑加上全部的贡献,然后求出最小的需要丢掉的贡献。
那么这个时候,如果没有限制就已经是最小割的典型模型了。
即直接建边
(
s
,
p
o
s
i
,
j
,
a
r
t
i
,
j
)
,
(
p
o
s
i
,
j
,
t
,
a
r
t
i
,
j
)
(s,pos_{i,j},art_{i,j}),(pos_{i,j},t,art_{i,j})
(s,posi,j,arti,j),(posi,j,t,arti,j)
这样求最小割是正确的。
现在有了满意值,怎么做呢?
直接分类讨论就行了。
- 学文且周围的学文,会损失 s c i e n c e + s a m e science+same science+same_ s c i e n c e science science那么多贡献。
- 学文且周围的学理,会损失 s a m e same same _ a r t + s c i e n c e + s a m e art+science+same art+science+same_ s c i e n c e science science那么多贡献。
- 学文且周围的不全学文,会损失 s a m e same same _ a r t + s c i e n c e + s a m e art+science+same art+science+same_ s c i e n c e science science那么多贡献。
学理是同理的。
按照这个来建边就行了:
(
s
,
p
o
s
i
,
j
′
,
s
a
m
e
(s,pos'_{i,j},same
(s,posi,j′,same_
a
r
t
i
,
j
)
,
(
p
o
s
i
,
j
′
,
p
o
s
i
′
,
j
′
,
i
n
f
)
art_{i,j}),(pos'_{i,j},pos_{i',j'},inf)
arti,j),(posi,j′,posi′,j′,inf),其中
(
i
,
j
)
,
(
i
′
,
j
′
)
(i,j),(i',j')
(i,j),(i′,j′)是相邻的。
理科同理。
这样子割出来一定是对的。
可以画个图来分类验证。
代码:
#include<bits/stdc++.h>
#define M 1000005
#define N 40005
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n,m,tim[505][505],Clocks=0,ans=0,dx[5]={0,0,0,-1,1},dy[5]={0,-1,1,0,0};
struct edge{int v,next,c;};
struct Dinic{
edge e[M<<1];
int first[N],cur[N],cnt,s,t,d[N];
inline void addedge(int u,int v,int c){e[++cnt].v=v,e[cnt].c=c,e[cnt].next=first[u],first[u]=cnt;}
inline void add(int u,int v,int c){addedge(u,v,c),addedge(v,u,0);}
inline void init(){memset(first,-1,sizeof(first)),cnt=-1,s=0,t=n*m*3+1;}
inline bool bfs(){
queue<int>q;
for(int i=1;i<=t;++i)d[i]=-1;
q.push(s);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=first[x];~i;i=e[i].next){
int v=e[i].v;
if(~d[v]||!e[i].c)continue;
d[v]=d[x]+1,q.push(v);
}
}
return ~d[t];
}
inline int dfs(int x,int f){
if(x==t||!f)return f;
int flow=f;
for(int&i=cur[x];~i;i=e[i].next){
int v=e[i].v;
if(!flow)break;
if(e[i].c&&d[v]==d[x]+1){
int tmp=dfs(v,min(flow,e[i].c));
if(!tmp)d[v]=-1;
e[i].c-=tmp,e[i^1].c+=tmp,flow-=tmp;
}
}
return f-flow;
}
inline int solve(){
int ret=0;
while(bfs())memcpy(cur,first,sizeof(first)),ret+=dfs(s,0x3f3f3f3f);
return ret;
}
}dinic;
int main(){
n=read(),m=read(),dinic.init();
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)tim[i][j]=++Clocks;
for(int i=1;i<=n;i++)for(int val,j=1;j<=m;++j)val=read(),dinic.add(dinic.s,tim[i][j],val),ans+=val;
for(int i=1;i<=n;++i)for(int val,j=1;j<=m;++j)val=read(),dinic.add(tim[i][j],dinic.t,val),ans+=val;
for(int i=1;i<=n;++i)for(int val,j=1;j<=m;++j){
val=read(),dinic.add(dinic.s,tim[i][j]+n*m,val),ans+=val;
for(int k=0;k<=4;++k){
int mx=dx[k]+i,my=dy[k]+j;
if(!mx||!my||mx>n||my>m)continue;
dinic.add(tim[i][j]+n*m,tim[mx][my],0x3f3f3f3f);
}
}
for(int i=1;i<=n;++i)for(int val,j=1;j<=m;++j){
val=read(),dinic.add(tim[i][j]+2*n*m,dinic.t,val),ans+=val;
for(int k=0;k<=4;++k){
int mx=dx[k]+i,my=dy[k]+j;
if(!mx||!my||mx>n||my>m)continue;
dinic.add(tim[mx][my],tim[i][j]+2*n*m,0x3f3f3f3f);
}
}
cout<<ans-dinic.solve();
return 0;
}