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));
}

你可能感兴趣的:(BZOJ 3894: 文理分科 最小割)