\(考虑用最小割表示所有边权中满足一个同学只有文或理所去掉的边的最小值\)
\(所以答案即为边权和-最小割,相当于是选择一些边保留然后保证选择的权值符合条件\)
\(再考虑如果没有相邻相同就累加边权和的条件\)
\(如图,即求原图的最小割就等同于全选理保留的权或者全选文保留的权\)
\(再考虑带有相邻全相同加权的情况\)
\(问题便转化为几个点如果不是全连源或全连汇点便需要割掉一条边权值为相同的收益\)
\(于是建一个虚点\)
\(其中,必须这几个点全部割掉才不需要割掉虚点边,否则必须割\)
\(如图,粉色的点即为虚点\)
\(如果(8,11)断,(7,11)断,那么就会有一条路径(1,3)(3,7)(7,11)于是(1,3)必须断\)
\(而(4,11)(5,11)(6,11)(7,11)断,那么(1,3)便可以不断\)
\(这便符合先前的条件\)
\(于是,我们把虚点于实点连INF,最后跑最大流即可\)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN = 100005;
const int MAXM = 300005;
int head[MAXN];
int cnt = 1;
struct Edge {
int val;
int fval;
int to, nex;
} edge[MAXM];
void Add(int u, int v, int w) {
edge[++cnt].to = v;
edge[cnt].val = w;
edge[cnt].fval=w;
edge[cnt].nex = head[u];
head[u] = cnt;
}
int s, t,N;
int dis[MAXN];
int gap[MAXN];
void bfs()
{
memset(dis,-1,sizeof(dis));
memset(gap,0,sizeof(gap));
dis[t]=0;
gap[dis[t]]++;
queue<int>q;
q.push(t);
while(q.size())
{
int temp=q.front();
q.pop();
for(int i=head[temp];i;i=edge[i].nex)
{
int v=edge[i].to;
if(dis[v]==-1)
{
dis[v]=dis[temp]+1;
gap[dis[v]]++;
q.push(v);
}
}
}
}
int dfs_ISPA(int x,long long rest)
{
if(x==t)
{
return rest;
}
int used=0;
for(int i=head[x];i;i=edge[i].nex)
{
int v=edge[i].to;
int w=edge[i].val;
if(w&&dis[v]+1==dis[x])
{
int now=dfs_ISPA(v,min(rest-used,(long long)w));
if(!now)
{
continue;
}
used+=now;
edge[i].val-=now;
edge[i^1].val+=now;
if(used==rest)
{
return used;
}
}
}
gap[dis[x]]--;
if(gap[dis[x]]==0)
{
dis[s]=N+1;
}
dis[x]++;
gap[dis[x]]++;
return used;
}
int ISAP()
{
int Flow=0;
bfs();
while(dis[s]<=N)
{
Flow+=dfs_ISPA(s,INF);
}
return Flow;
}
int n,m;
int x;
int zfx[15]={0,0,0,1,-1};
int zfy[15]={0,1,-1,0,0};
int res=0;
int main() {
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&x);
Add(0,n*m+(i-1)*m+j,x);
Add(n*m+(i-1)*m+j,0,0);
res+=x;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&x);
res+=x;
Add(n*m+(i-1)*m+j,3*n*m+1,x);
Add(3*n*m+1,n*m+(i-1)*m+j,0);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&x);
res+=x;
Add(0,(i-1)*m+j,x);
Add((i-1)*m+j,0,0);
for(int k=0;k<=4;k++)
{
int nx=i+zfx[k];
int ny=j+zfy[k];
if(nx>=1&&ny>=1&&nx<=n&&ny<=m)
{
Add((i-1)*m+j,n*m+(nx-1)*m+ny,INF);
Add(n*m+(nx-1)*m+ny,(i-1)*m+j,0);
}
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&x);
res+=x;
Add(2*n*m+(i-1)*m+j,3*n*m+1,x);
Add(3*n*m+1,2*n*m+(i-1)*m+j,0);
for(int k=0;k<=4;k++)
{
int nx=i+zfx[k];
int ny=j+zfy[k];
if(nx>=1&&ny>=1&&nx<=n&&ny<=m)
{
Add(n*m+(nx-1)*m+ny,2*n*m+(i-1)*m+j,INF);
Add(2*n*m+(i-1)*m+j,n*m+(nx-1)*m+ny,0);
}
}
}
}
s=0;
t=3*n*m+1;
N=t-s+1;
printf("%d",res-ISAP());
}