[BZOJ 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想知道,大家应该如何选择,才能使所有人的满意值之和最大。请
告诉他这个最大值。

Solution

 妹主席在JZYZ讲过的一道,想起来了于是去切一下

“依然是把最大化收益转化为最小化得不到的收益。
对于每个点,向S和T分别连容量为ai,bi的边,那么V代表划分到A集合的点,V’代表c[s,V’]代表划分到B集合中的元素失去的ai收益,c[V,t]相反,因为选到A集合的点和选到B集合的点相互之间不会产生影响,所以c[V,V’]=0
对于每个附加条件我们额外建立一个点,假设是要求一些元素必须划分到A集合中,那么从源点向它连一条容量为ci的边,从它向要求的元素连一条容量为正无穷的边,这样,割掉源点连向它的边(舍弃掉这个收益)和割掉它连向的所有点连向汇点的边(舍弃掉这些点划分到bi的收益)必须选择其中一个。
点数为O(n+m),边数为O(n+m+sigma(|S|))”
——妹主席的教诲

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define INF 0x3f3f3f3f
using namespace std;
int n,m,s,t,level[50005],head[50005],cnt=0,ans=0;
int dx[5]={1,-1,0,0,0},dy[5]={0,0,1,-1,0};
struct Node{
    int next,to,cap;
}Edges[1000005];
int Read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-1;c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';c=getchar();
    }
    return x*f;
}
void addedge(int u,int v,int c)
{
    Edges[cnt].next=head[u];
    head[u]=cnt;
    Edges[cnt].to=v;
    Edges[cnt++].cap=c;
}
void Insert(int u,int v,int c)
{
    addedge(u,v,c);
    addedge(v,u,0);
}
int number(int i,int j)
{
    return (i-1)*m+j;
}
bool bfs()
{
    memset(level,-1,sizeof(level));
    queue<int>q;
    q.push(s);level[s]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];~i;i=Edges[i].next)
        {
            int v=Edges[i].to;
            if(level[v]==-1&&Edges[i].cap)
            level[v]=level[u]+1,q.push(v);
        }
    }
    if(level[t]==-1)return false;
    return true;
}
int dfs(int u,int Maxflow)
{
    if(u==t)return Maxflow;
    int flow=0,d;
    for(int i=head[u];~i&&Maxflow>flow;i=Edges[i].next)
    {
        int v=Edges[i].to;
        if(level[v]==level[u]+1&&Edges[i].cap)
        {
            d=dfs(v,Min(Maxflow-flow,Edges[i].cap));
            Edges[i].cap-=d;
            Edges[i^1].cap+=d;
            flow+=d;
        }
    }
    if(!flow)level[u]=-1;
    return flow;
}
int Dinic()
{
    int res=0,d;
    while(bfs())
    {
        while(d=dfs(s,INF))
        res+=d;
    }
    return res;
}
int main()
{
    memset(head,-1,sizeof(head));
    n=Read(),m=Read();
    s=0,t=3*n*m+1;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        int art=Read();
        ans+=art;
        Insert(s,number(i,j),art);
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        int science=Read();
        ans+=science;
        Insert(number(i,j),t,science);
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        int same_art=Read(),p=number(i,j)+m*n;
        ans+=same_art;
        Insert(s,p,same_art);
        for(int k=0;k<5;k++)
        {
            if(i+dx[k]>0&&i+dx[k]<=n&&j+dy[k]>0&&j+dy[k]<=m)
            Insert(p,number(i+dx[k],j+dy[k]),INF);
        }
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        int same_science=Read(),p=number(i,j)+2*m*n;
        ans+=same_science;
        Insert(p,t,same_science);
        for(int k=0;k<5;k++)
        {
            if(i+dx[k]>0&&i+dx[k]<=n&&j+dy[k]>0&&j+dy[k]<=m)
            Insert(number(i+dx[k],j+dy[k]),p,INF);
        }
    }
    printf("%d\n",ans-Dinic());
    return 0;
}

 

posted @ 2017-04-11 18:43  Zars19  阅读(238)  评论(0编辑  收藏  举报