zoukankan      html  css  js  c++  java
  • [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
    ",ans-Dinic());
        return 0;
    }
  • 相关阅读:
    windows快捷键十八式(win10)
    解决滚动条突然出现导致的页面错位问题
    用命令行撤销工作区的所有更改(修改文件&&新增文件)
    用animation的steps属性制作帧动画
    配置Gitlab pages和Gitlab CI
    zookeeper中的分布式一致性协议
    kafka消息的处理机制(五)
    Paxos算法原理
    kafka同步异步消费和消息的偏移量(四)
    kafka客户端和服务端开发(三)
  • 原文地址:https://www.cnblogs.com/Zars19/p/6694937.html
Copyright © 2011-2022 走看看