链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3894
题解
S向每个人连边,容量是选文科的满意值;每个点向T连边,容量是选理科的满意值。
再新建2*N*M个点,表示每个人和相邻的人都选文(p1)、或都选理(p2)。S向p1连边,容量为这个人的same_art,p1再向这个人和相邻的四个人都连inf的边;p2向T连边,容量为这个人的same_science,这个人以及周围相邻的四个人向T2连边。
跑最小割。
代码
//最小割
#include <cstdio>
#include <algorithm>
#define maxn 300000
#define inf 0x3f3f3f3f
using namespace std;
int head[maxn], to[maxn], c[maxn], nex[maxn], N, a[150][150], s[150][150], sa[150][150],
ss[150][150], Exit, d[maxn], num[maxn], tot=1, S, T, last[maxn], M, ans;
void adde(int a, int b, int cc){to[++tot]=b;c[tot]=cc;nex[tot]=head[a];head[a]=tot;}
void adde2(int a, int b, int cc){adde(a,b,cc);adde(b,a,0);}
int isap(int pos, int in)
{
if(pos==T)return in;
int flow=0, t;
for(int &p=last[pos];p;p=nex[p])
if(c[p] and d[to[p]]+1==d[pos])
{
flow+= t=isap(to[p],min(c[p],in-flow));
c[p]-=t, c[p^1]+=t;
if(Exit or in==flow)return flow;
}
Exit=--num[d[pos]]==0;
++num[++d[pos]];
last[pos]=head[pos];
return flow;
}
int tab(int a, int b){return (a-1)*M+b;}
void init()
{
int i, j, t1, t2;
scanf("%d%d",&N,&M);
for(i=1;i<=N;i++)for(j=1;j<=M;j++)scanf("%d",a[i]+j);
for(i=1;i<=N;i++)for(j=1;j<=M;j++)scanf("%d",s[i]+j);
for(i=1;i<=N;i++)for(j=1;j<=M;j++)scanf("%d",sa[i]+j);
for(i=1;i<=N;i++)for(j=1;j<=M;j++)scanf("%d",ss[i]+j);
S=N*M*3+10, T=S+1;
for(i=1;i<=N;i++)for(j=1;j<=M;j++)
{
adde2(S,tab(i,j),a[i][j]);
adde2(tab(i,j),T,s[i][j]);
t1=N*M+tab(i,j), t2=N*M+N*M+tab(i,j);
adde2(S,t1,sa[i][j]), adde2(t2,T,ss[i][j]);
adde2(t1,tab(i,j),inf), adde2(tab(i,j),t2,inf);
if(i>1)adde2(t1,tab(i-1,j),inf),adde2(tab(i-1,j),t2,inf);
if(i<N)adde2(t1,tab(i+1,j),inf),adde2(tab(i+1,j),t2,inf);
if(j>1)adde2(t1,tab(i,j-1),inf),adde2(tab(i,j-1),t2,inf);
if(j<M)adde2(t1,tab(i,j+1),inf),adde2(tab(i,j+1),t2,inf);
}
for(i=1;i<=N;i++)for(j=1;j<=M;j++)ans+=a[i][j]+s[i][j]+sa[i][j]+ss[i][j];
}
int main()
{
init();
while(!Exit)ans-=isap(S,inf);
printf("%d",ans);
return 0;
}