zoukankan      html  css  js  c++  java
  • [置顶] [BZOJ]2127: happiness 最小割

    happiness:

    Description

    高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大。

    Input

    第一行两个正整数n,m。接下来是六个矩阵第一个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择文科获得的喜悦值。第二个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择理科获得的喜悦值。第三个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择文科获得的额外喜悦值。第四个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择理科获得的额外喜悦值。第五个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择文科获得的额外喜悦值。第六个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择理科获得的额外喜悦值。

    Output

    输出一个整数,表示喜悦值总和的最大值

    Sample Input

    1 2
    1 1
    100 110
    1
    1000 

    Sample Output

    1210




    【样例说明】
    两人都选理,则获得100+110+1000的喜悦值。
    【数据规模】
    对于100%以内的数据,n,m<=100 所有喜悦值均为小于等于5000的非负整数



       首先发现题目出现了很明显的二分图关系--选文科还是理科,很明显是一个网络流问题,联系到题目上的一个人选文科还是理科可以得到一定喜悦值,两个人同时选文科还是理科,又可以得到一定喜悦值,发现最终答案是由每个人选文科,还是选理科,这个行为确定的,与最小割有一定关系。

         继续观察,如果按最小割建立模型,每个人选文选理的代价可以在与s,t的连边上体现,这时难点就变成了如何体现两个人之间的关系:同时选文或选理需要的代价。

       这时引入一个很神奇的东西,无向边。

       这个东西是我在clj的ppt上看见的,有一个很有用的作用:若A<-->B:C,即A与B连一条权值为C的无向边,当这条无向边计入最小割时,表示这两个点分属于s,t两个集合。

       但是仅仅知道如此是不够的,因为对于A,B两点,如果他们都属于S集合,即与T集合的边已经计入最小割了,那A与B之间的边就一定不会出现在最小割里,这一点可以作图验证一二。

       所以我们应该对原图进行一定变换。注意到这里我们是想将两个点之间的关系体现在同时取s集或同时取t集的情况,即与s,与t的连接边:若A与B同时选文得到W1的喜悦值,同时选理得到W2的喜悦值,S->A:w1/2,A->T:w2/2,

    S->B:w1/2,B->T:w2/2,A<-->B:(w1+w2)/2。

       为什么呢?自己画图验证,很神奇的东西,特别难想,不过做过一遍以后再做就觉得很容易了。


    /**************************************************************
        Problem: 2127
        User: 1156603280
        Language: C++
        Result: Accepted
        Time:756 ms
        Memory:8620 kb
    ****************************************************************/
     
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <ctime>
    #include <cmath>
    #define maxn 10000
    using namespace std;
    int n,m;
    int s,t;
    int tot=1;
    int fir[200000],en[200000],nex[200000],f[200000];
    void ins(int a,int b,int c,int d){
        nex[++tot]=fir[a];
        fir[a]=tot;
        en[tot]=b;
        f[tot]=c;
         
        nex[++tot]=fir[b];
        fir[b]=tot;
        en[tot]=a;
        f[tot]=d;
     
    }
    int ch[200][200][2];
     
    int flow;
    int d[200000],now[200000],num[200000],pre[200000],his[200000];
    void  sap(){
        flow=0;
        for (int i=0;i<=t;i++){
            now[i]=fir[i];
            d[i]=num[i]=0;
            }
        num[0]=t;
        int aug=0x7fffffff;
        bool flag;
        int i=s;
        while (d[s]<t){
            his[i]=aug;
            flag=false;
            for (int k=now[i];k;k=nex[k])
                if (f[k]>0&&d[i]==d[en[k]]+1){
                    aug=min(aug,f[k]);
                    flag=true;
                    now[i]=k;
                    pre[en[k]]=i;
                    i=en[k];
                    if (i==t){
                        flow+=aug;
                        while (i!=s){
                            i=pre[i];
                            f[now[i]]-=aug;
                            f[now[i]^1]+=aug;
                            }
                        aug=0x7fffffff;
                        }
                    break;
                    }
            if (flag) continue;
            int k1=0,minn=t;
            for (int k=fir[i];k;k=nex[k])
                if (f[k]>0&&minn>d[en[k]]){
                    k1=k;
                    minn=d[en[k]];
                    }
            now[i]=k1;
            if (!--num[d[i]]) return;
            d[i]=minn+1;
            num[d[i]]++;
             
            if (i!=s){
                i=pre[i];
                aug=his[i];
                }
            }
     
     
    }
     
    int sum=0;
    int main(){
    //  freopen("2127.in","r",stdin);
    //  freopen("2127.out","w",stdout);
        scanf("%d%d",&n,&m);
        s=n*m+1,t=n*m+2;
        for (int i=1;i<=n;i++)           //割文  去理
            for (int j=1;j<=m;j++){
                scanf("%d",&ch[i][j][0]);
                sum+=ch[i][j][0];
                ch[i][j][0]*=2;
                }
        for (int i=1;i<=n;i++)           //割理  去文
            for (int j=1;j<=m;j++){
                scanf("%d",&ch[i][j][1]);
                sum+=ch[i][j][1];
                ch[i][j][1]*=2;
                }
     
     
         
        for (int i=1;i<=n-1;i++)
            for (int j=1;j<=m;j++){
                int tmp;
                scanf("%d",&tmp);
                sum+=tmp;
                ins((i-1)*m+j,i*m+j,tmp,tmp);
                ch[i][j][0]+=tmp;
                ch[i+1][j][0]+=tmp;
                }
        for (int i=1;i<=n-1;i++)
            for (int j=1;j<=m;j++){
                int tmp;
                scanf("%d",&tmp);
                sum+=tmp;
                ins((i-1)*m+j,i*m+j,tmp,tmp);
                ch[i][j][1]+=tmp;
                ch[i+1][j][1]+=tmp;
                }
         
         
         
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m-1;j++){
                int tmp;
                scanf("%d",&tmp);
                sum+=tmp;
                ins((i-1)*m+j,(i-1)*m+j+1,tmp,tmp);
                ch[i][j][0]+=tmp;
                ch[i][j+1][0]+=tmp;
                }
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m-1;j++){
                int tmp;
                scanf("%d",&tmp);
                sum+=tmp;
                ins((i-1)*m+j,(i-1)*m+j+1,tmp,tmp);
                ch[i][j][1]+=tmp;
                ch[i][j+1][1]+=tmp;
                }
                 
                 
         
         
         
         
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++){
                ins(s,(i-1)*m+j,ch[i][j][0],0);
                ins((i-1)*m+j,t,ch[i][j][1],0);
                }
        sap();
         
        printf("%d",sum-flow/2);
     
        return 0;
    }


  • 相关阅读:
    布局神器display:table-cell
    解决IE兼容总汇【转】
    jQuery Validate【强大的表单验证】
    使用信号SIGALRM 为 recvfrom 设置超时,防止死等待
    并发服务器(IO多路复用)
    并发服务器(多进程版本)
    TCP listen()函数内幕
    c++ 重载运算符规则
    内核定时器struct timer_list
    C和arm汇编的相互调用(看书笔记)
  • 原文地址:https://www.cnblogs.com/fuhaots2009/p/3481846.html
Copyright © 2011-2022 走看看