题意:
有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; }