好题。
去年夏令营的时候似乎davidwang神犇讲过,我一定是睡过去了qwq。。
时隔半年来做一下这道题。
首先我们不考虑相邻的相同有加成的情况,那么每个人只对应文和理两种情况,如果用最小割(不要和我说贪心)的思路来解决,也就是将所有人分到不同的集合,一个为S集,假设代表学文科,另一个为T集,假设代表学理科。Ai代表i选文的喜悦值,Bi代表i选理的喜悦值。那么这就成了一道noip普(xiao)及(xue)组的题,我们可以如下建图:
四种合法的割集{A1,A2} {A1,B2} {B1,B2} {A2,B1}分别代表了四种合法的方案。拿到本题中来说,{A1,B2} {A2,B1}也代表了1和2的选择不同。但是现在的问题是,{A1,A2} {B1,B2}这两种割集并没有对应到同时选文或同时选理的喜悦值。
我们拿第一种情况来考虑,如果选了{A1,A2},那么我们应当取得的是B1,B2和1、2共同选理的喜悦值,应当舍弃的是A1,A2和1、2同时
选文的喜悦值,因为是由1、2共同决定的,我们不妨改变一下边的权值。这里用C[i][j]代表i和j共同选文的喜悦值,D[i][j]代表i和j共同选理的喜悦值。那么我们可以如下建图:
这样刚才的问题就解决了,{A1,A2} {B1,B2}这两种割集有了合法的意义。
我们再回头看一下刚才的{A1,B2} {A2,B1},发现我们多获得了一些喜悦值,那就是C[1][2]/2+D[1][2]/2。为了减去这一部分,我们可以再连一条边,使这条边的权值为C[1][2]/2+D[1][2]/2,并且要保证在这种方案下这条边一定能被割掉。那么我们可以如下建图:
这样这个问题就得到了解决,并且发现刚才那种情况也不会受到影响。所以这种建图是合法的。
需要注意的是除以二可能会出问题,可以统一乘二。
今天手感不错?写完编译样例AC异常顺畅QAQ。。
#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 2147483647
using namespace std;
int n,m,cnt=1,sum,S,T;
int head[10005],dis[10005],cur[10005],q[10005];
int next[1200005],list[1200005],key[1200005];
int a[105][105],b[105][105];
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
inline void insert(int x,int y,int z)
{
next[++cnt]=head[x];
head[x]=cnt;
list[cnt]=y;
key[cnt]=z;
}
inline int c(int i,int j)
{
return (i-1)*m+j;
}
inline void pre()
{
n=read(); m=read(); S=0; T=n*m+1;
int x;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j]=read(),sum+=a[i][j],a[i][j]<<=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
b[i][j]=read(),sum+=b[i][j],b[i][j]<<=1;
for (int i=1;i<n;i++)
for (int j=1;j<=m;j++)
x=read(),sum+=x,a[i][j]+=x,a[i+1][j]+=x,insert(c(i,j),c(i+1,j),x),insert(c(i+1,j),c(i,j),x);
for (int i=1;i<n;i++)
for (int j=1;j<=m;j++)
x=read(),sum+=x,b[i][j]+=x,b[i+1][j]+=x,insert(c(i,j),c(i+1,j),x),insert(c(i+1,j),c(i,j),x);
for (int i=1;i<=n;i++)
for (int j=1;j<m;j++)
x=read(),sum+=x,a[i][j]+=x,a[i][j+1]+=x,insert(c(i,j),c(i,j+1),x),insert(c(i,j+1),c(i,j),x);
for (int i=1;i<=n;i++)
for (int j=1;j<m;j++)
x=read(),sum+=x,b[i][j]+=x,b[i][j+1]+=x,insert(c(i,j),c(i,j+1),x),insert(c(i,j+1),c(i,j),x);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
insert(S,c(i,j),a[i][j]),insert(c(i,j),S,0),insert(c(i,j),T,b[i][j]),insert(T,c(i,j),0);
}
inline bool BFS()
{
memset(dis,-1,sizeof(dis));
dis[S]=1; q[1]=S;
int t=0,w=1,x;
while (t<w)
{
x=q[++t];
for (int i=head[x];i;i=next[i])
if (key[i]&&dis[list[i]]==-1)
dis[list[i]]=dis[x]+1,q[++w]=list[i];
}
return dis[T]!=-1;
}
int find(int x,int flow)
{
if (x==T) return flow;
int w,used=0;
for (int i=cur[x];i;i=next[i])
if (key[i]&&dis[list[i]]==dis[x]+1)
{
w=find(list[i],min(flow-used,key[i]));
key[i]-=w; key[i^1]+=w; used+=w;
if (key[i]) cur[x]=i;
if (used==flow) return flow;
}
if (!used) dis[x]=-1;
return used;
}
inline int dinic()
{
int tmp=0;
while (BFS())
{
for (int i=S;i<=T;i++) cur[i]=head[i];
tmp+=find(S,inf);
}
return tmp;
}
int main()
{
pre();
printf("%d\n",sum-(dinic()>>1));
return 0;
}