题意:
有n行m列,每个位置有一个人,每个人有一个选文科的喜悦值和一个选理科的喜悦值。另外,如果前后左右相邻的两个人同选了文科或者同选了理科,会获得一个额外喜悦值。现在要求最大能获得的喜悦值。
这题似乎有人称之为文理分科?
一个人当然只能从文科和理科中选一个,就相当于一部分人选了文科,一部分人选了理科,变成了一个集合划分的问题。
我听说了好几种建边的方法,我只说一下自己用的方法,因为别的方法都没有仔细考虑过。
感觉这种把一些事物划分到两个集合的题经常是最小割,这题就是。
本题先求出所有喜悦值之和,再用最小割求出最小喜悦值损失。
首先我们考虑一个人要么选文科,要么选理科,所以我们对于每个人,从源点向这个人连选文科的喜悦值,并从这个人向汇点连选理科的喜悦值,这样文理一定会割掉一条。
再考虑相邻带来的额外喜悦值。我这里采用了新建一些节点的方法,对于每相邻的两个人,新建出两个新点,从源点向第一个新点连同选文科的喜悦值,然后从这个新点向这相邻的两个人分别连流量正无穷的边(不可割掉),再从这两个相邻的人分别向第二个新点连流量为正无穷的边,最后从第二个新点向汇点连流量为同选理科的额外喜悦值的边。
这样就建好图了,跑完最小割之后用喜悦值总和减去最小割就是答案。
如果不理解的话可以画个图看看,样例数据的图就不错,你会发现这样割出来的一定是对的。
这样建出来的图,对于任意两个相邻的人,要么把选理科和同选理科的喜悦割掉,要么把选文科和同选文科的喜悦割掉,要么割掉一人的文、另一人的理和两人同文同理的喜悦。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,st,ed,hed[500001],cnt,ji;
int res,dep[50009],h,t,q[50009],sum;//dep有点大,跑得比较慢
struct node
{
int to,next,c;
}a[5000001];
void add(int from,int to,int c)
{
a[++cnt].to=to;
a[cnt].c=c;
a[cnt].next=hed[from];
hed[from]=cnt;
a[++cnt].to=from;
a[cnt].c=0;
a[cnt].next=hed[to];
hed[to]=cnt;
}
int bfs()
{
memset(dep,0,sizeof(dep));
q[1]=st;
h=1;t=2;
dep[st]=1;
while(h!=t)
{
int x=q[h];
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(a[i].c&&dep[y]==0)
{
dep[y]=dep[x]+1;
q[t++]=y;
}
}
h++;
}
if(dep[ed]>0)
return 1;
else
return 0;
}
int flow(int x,int f)
{
if(x==ed)
return f;
int s=0,t;
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(a[i].c&&dep[y]==dep[x]+1&&s<f)
{
s+=(t=flow(y,min(a[i].c,f-s)));
a[i].c-=t;
a[i^1].c+=t;
}
}
if(s==0)
dep[x]=0;
return s;
}
int main()
{
scanf("%d%d",&n,&m);
cnt=1;
st=50000,ed=50001;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int x;
scanf("%d",&x);
add(st,(i-1)*m+j,x);
sum+=x;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int x;
scanf("%d",&x);
add((i-1)*m+j,ed,x);
sum+=x;
}
}
ji=n*m;
for(int i=1;i<n;i++)
{
for(int j=1;j<=m;j++)
{
int x;
scanf("%d",&x);
add(st,++ji,x);
add(ji,(i-1)*m+j,2e9);
add(ji,i*m+j,2e9);
sum+=x;
}
}
for(int i=1;i<n;i++)
{
for(int j=1;j<=m;j++)
{
int x;
scanf("%d",&x);
add(++ji,ed,x);
add((i-1)*m+j,ji,2e9);
add(i*m+j,ji,2e9);
sum+=x;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<m;j++)
{
int x;
scanf("%d",&x);
add(st,++ji,x);
add(ji,(i-1)*m+j,2e9);
add(ji,(i-1)*m+j+1,2e9);
sum+=x;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<m;j++)
{
int x;
scanf("%d",&x);
add(++ji,ed,x);
add((i-1)*m+j,ji,2e9);
add((i-1)*m+j+1,ji,2e9);
sum+=x;
}
}
while(bfs())
res+=flow(st,2e9);
printf("%d\n",sum-res);
return 0;
}