BZOJ 3894: 文理分科 最小割
3894: 文理分科
题目连接:
http://www.lydsy.com/JudgeOnline/problem.php?id=3894
Description
文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠
结过)
小P所在的班级要进行文理分科。他的班级可以用一个n*m的矩阵进行
描述,每个格子代表一个同学的座位。每位同学必须从文科和理科中选择
一科。同学们在选择科目的时候会获得一个满意值。满意值按如下的方式
得到:
1.如果第i行第秒J的同学选择了文科,则他将获得art[i][j]的满意值,如
果选择理科,将得到science[i][j]的满意值。
2.如果第i行第J列的同学选择了文科,并且他相邻(两个格子相邻当且
仅当它们拥有一条相同的边)的同学全部选择了文科,则他会更开
心,所以会增加same_art[i][j]的满意值。
3.如果第i行第j列的同学选择了理科,并且他相邻的同学全部选择了理
科,则增加same_science[i]j[]的满意值。
小P想知道,大家应该如何选择,才能使所有人的满意值之和最大。请
告诉他这个最大值。
Input
第一行为两个正整数:n,m
接下来n术m个整数,表示art[i][j];
接下来n术m个整数.表示science[i][j];
接下来n术m个整数,表示same_art[i][j];
Output
输出为一个整数,表示最大的满意值之和
Sample Input
3 4
13 2 4 13
7 13 8 12
18 17 0 5
8 13 15 4
11 3 8 11
11 18 6 5
1 2 3 4
4 2 3 2
3 1 0 4
3 2 3 2
0 2 2 1
0 2 4 4
Sample Output
152
Hint
题意
题解
最小割
连接S的表示选择文科,连接T的表示选择理科
对于周围的十字,那么就是表示这五个人都选的文科或者理科。
那我们连一个特殊的点就好了呀,然后文科的十字连接这个点以及这个点周围的点就好了。
理科同理。
具体建图看代码,用最小割的思想去理解,就比较简单了。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=500000,MAXM=500000,inf=1e9;
struct Edge
{
int v,c,f,nx;
Edge() {}
Edge(int v,int c,int f,int nx):v(v),c(c),f(f),nx(nx) {}
} E[MAXM];
int G[MAXN],cur[MAXN],pre[MAXN],dis[MAXN],gap[MAXN],N,sz;
void init(int _n)
{
N=_n,sz=0; memset(G,-1,sizeof(G[0])*N);
}
void link(int u,int v,int c)
{
E[sz]=Edge(v,c,0,G[u]); G[u]=sz++;
E[sz]=Edge(u,0,0,G[v]); G[v]=sz++;
}
bool bfs(int S,int T)
{
static int Q[MAXN]; memset(dis,-1,sizeof(dis[0])*N);
dis[S]=0; Q[0]=S;
for (int h=0,t=1,u,v,it;h<t;++h)
{
for (u=Q[h],it=G[u];~it;it=E[it].nx)
{
if (dis[v=E[it].v]==-1&&E[it].c>E[it].f)
{
dis[v]=dis[u]+1; Q[t++]=v;
}
}
}
return dis[T]!=-1;
}
int dfs(int u,int T,int low)
{
if (u==T) return low;
int ret=0,tmp,v;
for (int &it=cur[u];~it&&ret<low;it=E[it].nx)
{
if (dis[v=E[it].v]==dis[u]+1&&E[it].c>E[it].f)
{
if (tmp=dfs(v,T,min(low-ret,E[it].c-E[it].f)))
{
ret+=tmp; E[it].f+=tmp; E[it^1].f-=tmp;
}
}
}
if (!ret) dis[u]=-1; return ret;
}
int dinic(int S,int T)
{
int maxflow=0,tmp;
while (bfs(S,T))
{
memcpy(cur,G,sizeof(G[0])*N);
while (tmp=dfs(S,T,inf)) maxflow+=tmp;
}
return maxflow;
}
int n,m;
int dx[5]={1,-1,0,0,0};
int dy[5]={0,0,1,-1,0};
int id(int x,int y){
return (x-1)*m+y;
}
int main(){
scanf("%d%d",&n,&m);
int S=0,T=n*m*3+1;
init(n*m*3+5);
int ans = 0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int a;scanf("%d",&a);
ans+=a;
link(S,id(i,j),a);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int a;scanf("%d",&a);
ans+=a;
link(id(i,j),T,a);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int a;scanf("%d",&a);
ans+=a;
link(S,id(i,j)+m*n,a);
for(int k=0;k<5;k++){
int x=dx[k]+i;
int y=dy[k]+j;
if(x<1||x>n||y<1||y>m)continue;
link(id(i,j)+m*n,id(x,y),inf);
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int a;scanf("%d",&a);
ans+=a;
link(id(i,j)+2*m*n,T,a);
for(int k=0;k<5;k++){
int x=dx[k]+i;
int y=dy[k]+j;
if(x<1||x>n||y<1||y>m)continue;
link(id(x,y),id(i,j)+m*n*2,inf);
}
}
}
printf("%d\n",ans-dinic(S,T));
}