传送门
最小割,割掉的是舍弃的喜悦值。
从s向每一个人连边,容量为这个人学文的喜悦值;从每个人向t连边,容量为这个人学理的喜悦值。
处理两个人共同学文或学理的喜悦值的话,可以每两个人增加一个结点,然后将两个人都向这个点连边,容量为学文或学理的喜悦值,注意这个点还要向s或t连边,容量也为学文或学理的喜悦值。
即对于每两个有关系的人增加两个结点,分别处理共同学文或者共同学理的情况,限制为各自的喜悦值。注意s方向向人得方向连的边意义都为学文,而从人的方向向t方向连的边意义都为学理。
最终用总的喜悦值减去maxflow即为答案。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int max_n=105;
const int max_m=105;
const int max_N=max_n*max_m+2*((max_n-1)*max_m+max_n*(max_m-1))+2+5;
const int max_e=2*max_n*max_m+3*2*((max_n-1)*max_m+max_n*(max_m-1))+5;
const int inf=1e9;
int n,m,maxflow,N,sum;
int wen[max_n][max_m],li[max_n][max_m],weni[max_n][max_m],lii[max_n][max_m],wenj[max_n][max_m],lij[max_n][max_m];
int cnt,num[max_n][max_m];
int tot,point[max_N],next[max_e*2],v[max_e*2],remain[max_e*2];
int deep[max_N],cur[max_N];
queue <int> q;
inline void add(int x,int y,int cap){
++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap;
++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}
inline bool bfs(int s,int t){
memset(deep,0x7f,sizeof(deep));
deep[s]=0;
for (int i=1;i<=N;++i)
cur[i]=point[i];
while (!q.empty()) q.pop();
q.push(s);
while (!q.empty()){
int now=q.front(); q.pop();
for (int i=point[now];i!=-1;i=next[i])
if (deep[v[i]]>inf&&remain[i]){
deep[v[i]]=deep[now]+1;
q.push(v[i]);
}
}
return deep[t]<inf;
}
inline int dfs(int now,int t,int limit){
if (now==t||!limit) return limit;
int flow=0,f;
for (int i=cur[now];i!=-1;i=next[i]){
cur[now]=i;
if (deep[v[i]]==deep[now]+1&&(f=dfs(v[i],t,min(remain[i],limit)))){
flow+=f;
limit-=f;
remain[i]-=f;
remain[i^1]+=f;
if (!limit) break;//
}
}
return flow;
}
inline void dinic(int s,int t){
while (bfs(s,t))
maxflow+=dfs(s,t,inf);
}
int main(){
//read
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
scanf("%d",&wen[i][j]),sum+=wen[i][j];
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
scanf("%d",&li[i][j]),sum+=li[i][j];
for (int i=1;i<n;++i)
for (int j=1;j<=m;++j)
scanf("%d",&weni[i][j]),sum+=weni[i][j];
for (int i=1;i<n;++i)
for (int j=1;j<=m;++j)
scanf("%d",&lii[i][j]),sum+=lii[i][j];
for (int i=1;i<=n;++i)
for (int j=1;j<m;++j)
scanf("%d",&wenj[i][j]),sum+=wenj[i][j];
for (int i=1;i<=n;++i)
for (int j=1;j<m;++j)
scanf("%d",&lij[i][j]),sum+=lij[i][j];
//build up
tot=-1;
memset(point,-1,sizeof(point));
memset(next,-1,sizeof(next));
N=n*m+2*((n-1)*m+n*(m-1))+2;
cnt=1;
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j){
cnt++;
num[i][j]=cnt;
add(1,cnt,wen[i][j]);
add(cnt,N,li[i][j]);
}
for (int i=1;i<n;++i)
for (int j=1;j<=m;++j){
cnt++;
add(1,cnt,weni[i][j]);
add(cnt,num[i][j],weni[i][j]);
add(cnt,num[i+1][j],weni[i][j]);
}
for (int i=1;i<n;++i)
for (int j=1;j<=m;++j){
cnt++;
add(cnt,N,lii[i][j]);
add(num[i][j],cnt,lii[i][j]);
add(num[i+1][j],cnt,lii[i][j]);
}
for (int i=1;i<=n;++i)
for (int j=1;j<m;++j){
cnt++;
add(1,cnt,wenj[i][j]);
add(cnt,num[i][j],wenj[i][j]);
add(cnt,num[i][j+1],wenj[i][j]);
}
for (int i=1;i<=n;++i)
for (int j=1;j<m;++j){
cnt++;
add(cnt,N,lij[i][j]);
add(num[i][j],cnt,lij[i][j]);
add(num[i][j+1],cnt,lij[i][j]);
}
dinic(1,N);
printf("%d\n",sum-maxflow);
}