传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3894
思路:令S为学文,T为学理
对于额外的收入,新加一个点
也可以用二元组建图来解决
还是这个图,x表示一个学生,与S连通表示学文,与T连通表示学理
y表示新加的点,用来计算同时学理的额外收益,与S连通表示不要额外收益,与T连通表示要额外收益
先算出总收益,在用最小割减去即可
解方程:
c+d=B[i]+WB//学生i学文,不要额外收益,损失学理收益,损失额外收益
a+b=A[i]//学生i学理,要额外收益,损失学文收益
b+e+c=inf//学生i学文,要额外收益,这不可能,损失为inf
a+f+d=A[i]+WB//学生i学理,不要额外收益,损失学文收益,损失额外收益
a=A[i]
b=0
c=B[i]
d=WB
e=inf
f=0
学文类似,但要注意一些边方向不同,收益也不同
所以最终建图就是:
S向每个学生连边,容量为学文的收益,每个学生向T连边,容量为学理的收益
S向每个代表相邻5个人都学文收益点连边,容量为同时学文的收益
该点再向对应5个人连边,容量为inf,表示只要有人学理,就必须割掉同时学文的收益
学理类似
对应5个人向该点连inf的边,该点向T连同时学理的收益的边
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=30010,maxm=1000010,inf=1061109567;
const int dx[]={0,0,0,1,-1};
const int dy[]={0,1,-1,0,0};
using namespace std;
int n,m,sum;
int stu(int x,int y){return (x-1)*m+y;}
int art(int x,int y){return (x-1)*m+y+n*m;}
int sci(int x,int y){return (x-1)*m+y+n*m*2;}
struct Flow{
int pre[maxm],now[maxn],son[maxm],val[maxm],tot,dis[maxn],q[maxn+10],head,tail,S,T;
void add(int a,int b,int c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;}
void ins(int a,int b,int c){add(a,b,c),add(b,a,0);}
void init(){memset(now,0,sizeof(now)),tot=1,S=0,T=3*n*m+1;}
void build(){
for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&x),sum+=x,ins(S,stu(i,j),x);
for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&x),sum+=x,ins(stu(i,j),T,x);
for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++){
scanf("%d",&x),sum+=x;
for (int k=0;k<5;k++){
int nx=i+dx[k],ny=j+dy[k];
if (nx<1||nx>n||ny<1||ny>m) continue;
ins(art(i,j),stu(nx,ny),inf);
}
ins(S,art(i,j),x);
}
for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++){
scanf("%d",&x),sum+=x;
for (int k=0;k<5;k++){
int nx=i+dx[k],ny=j+dy[k];
if (nx<1||nx>n||ny<1||ny>m) continue;
ins(stu(nx,ny),sci(i,j),inf);
}
ins(sci(i,j),T,x);
}
}
bool bfs(){
memset(dis,-1,sizeof(dis));
q[tail=1]=S,dis[S]=head=0;
while (head!=tail){
if (++head>maxn) head=1;
int x=q[head];
for (int y=now[x];y;y=pre[y])
if (val[y]&&dis[son[y]]==-1){
if (++tail>maxn) tail=1;
dis[son[y]]=dis[x]+1,q[tail]=son[y];
}
}
return dis[T]>0;
}
int find(int x,int low){
if (x==T) return low;
int y,res=0;
for (y=now[x];y;y=pre[y]){
if (dis[son[y]]!=dis[x]+1||!val[y]) continue;
int tmp=find(son[y],min(low,val[y]));
res+=tmp,low-=tmp,val[y]-=tmp,val[y^1]+=tmp;
if (!low) break;
}
if (!y) dis[x]=-1;
return res;
}
void work(){
while (bfs()) sum-=find(S,inf);
printf("%d\n",sum);
}
}F;
int main(){
scanf("%d%d",&n,&m),F.init();
F.build(),F.work();
return 0;
}