zoukankan      html  css  js  c++  java
  • HDU 1569 方格取数(2) (Dinic)

    方格取数(2)

    Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 3387    Accepted Submission(s): 1045


    Problem Description
    给你一个m*n的格子的棋盘,每个格子里面有一个非负数。
    从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
     
    Input
    包括多个测试实例,每个测试实例包括2整数m,n和m*n个非负数(m<=50,n<=50)
     
    Output
    对于每个测试实例,输出可能取得的最大的和
     
    Sample Input
    3 3 75 15 21 75 15 28 34 70 5
     
    Sample Output
    188
     
    Author
    ailyanlu
     
    Source
     
    Recommend
    8600
     

    给出一个 N * M 的矩阵,每个格放着一个非负数,要求选出一些数,使他们的和最大,要求是有相邻边的格子里的数不能同时选。

    先说,我压根没想过这事网络流……因为方格取数(1)是个状态压缩……

    看了题解,才明白的:

    这个题由于数据范围较大,所以状态压缩过不去,需要用网络流,我重复一遍人家的建图:

    我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有:

    最大点权独立集 + 最小点权覆盖集 = 总点权和,

    这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图,

    1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一条边,权值为该黑色点的权值,

    2,从白色的点向汇点连一条边,权值为该白色点的权值,

    3,然后,对于每一对相邻的黑白点,从黑点向白点连一条边,权值为无穷大。

    最后求最小割(最大流),即为最小点权覆盖集。

    因为我们求出的最小割集一定是从那些相邻的黑白点之间的边(也就是不能用的边,因为相邻的数不能同时选)中选出来的,且是最小代价,也就是说从方格中拿掉的数之和尽量小,那么剩下的数之和一定是最大的。

    我只能说,神奇的网络流!!!!Orz!!!!

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    
    using namespace std;
    
    const int VM=2520;
    const int EM=500010;
    const int INF=0x3f3f3f3f;
    
    struct Edge{
        int u,v,nxt;
        int flow;
    }edge[EM<<1];
    
    int n,m,cnt,head[VM];
    int src,des,dep[VM];
    
    void addedge(int cu,int cv,int cf){
        edge[cnt].u=cu;  edge[cnt].v=cv;  edge[cnt].flow=cf;
        edge[cnt].nxt=head[cu];  head[cu]=cnt++;
    
        edge[cnt].u=cv;  edge[cnt].v=cu;  edge[cnt].flow=0;
        edge[cnt].nxt=head[cv];  head[cv]=cnt++;
    }
    
    int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
    
    int legal(int i,int j,int k){
        int x=i+dir[k][0];
        int y=j+dir[k][1];
        return x>=1 && x<=n && y>=1 && y<=m;
    }
    
    int BFS(){  // 重新 建 图 (按 层数 建图)
        queue<int> q;
        while(!q.empty())
            q.pop();
        memset(dep,-1,sizeof(dep));
        dep[src]=0;
        q.push(src);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=head[u];i!=-1;i=edge[i].nxt){
                int v=edge[i].v;
                if(edge[i].flow>0 && dep[v]==-1){  // 如果 可以  可以到达 但 还没有 访问
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        return dep[des]!=-1;
    }
    /*
    int DFS(int u,int minx){  // 查找  路径上的 最小 的 流量
         if(u==des)
             return minx;
         int tmp;
         for(int i=head[u];i!=-1;i=edge[i].nxt){
             int v=edge[i].v;
             if(edge[i].flow>0 && dep[v]==dep[u]+1 && (tmp=DFS(v,min(minx,edge[i].flow)))){
                 edge[i].flow-=tmp;
                 edge[i^1].flow+=tmp;
                 return tmp;
             }
             //printf("!!!!!!\n");
         }
         return 0;
     }
    */
    
    int DFS(int u,int minx){
        int ans=0;
        if(u==des)
            return minx;
        for(int i=head[u];i!=-1 && ans<minx;i=edge[i].nxt){
            int v=edge[i].v;
            if(edge[i].flow>0 && dep[v]==dep[u]+1){
                int tmp=min(edge[i].flow,minx-ans);
                tmp=DFS(v,tmp);
                ans+=tmp;
                edge[i].flow-=tmp;
                edge[i^1].flow+=tmp;
            }
        }
        if(!ans)
            dep[u]=-2;
        return ans;
    }
    
    int Dinic(){
        int ans=0,tmp;
        while(BFS()){
            while(1){
                tmp=DFS(src,INF);
                if(tmp==0)
                    break;
                ans+=tmp;
            }
        }
        return ans;
    }
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
        while(~scanf("%d%d",&n,&m)){
            cnt=0;
            memset(head,-1,sizeof(head));
            int x,sum=0;
            src=0;  des=n*m+1;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++){
                    scanf("%d",&x);
                    sum+=x;
                    if((i+j)%2==0){
                        addedge(src,(i-1)*m+j,x);
                        for(int k=0;k<4;k++){
                            if(legal(i,j,k))
                                addedge((i-1)*m+j,(i+dir[k][0]-1)*m+(j+dir[k][1]),INF);
                        }
                    }else{
                        addedge((i-1)*m+j,des,x);
                        for(int k=0;k<4;k++){
                            if(legal(i,j,k))
                                addedge((i+dir[k][0]-1)*m+(j+dir[k][1]),(i-1)*m+j,INF);
                        }
                    }
                }
            int maxflow=Dinic();
            printf("%d\n",sum-maxflow);
        }
        return 0;
    }
  • 相关阅读:
    最大子数组求和并进行条件组合覆盖测试
    Ubuntu 16.04 c++ Google框架单元测试
    The directory '/home/stone/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If execu
    Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/cache/app-info -a -e /usr/bin/appstreamcli; then appstreamcli refresh > /dev/null; fi'
    个人博客作业三:微软小娜APP的案例分析
    补交 作业一
    补交 作业二:个人博客作业内容:需求分析
    嵌入式软件设计第12次实验报告
    嵌入式软件设计第11次实验报告
    嵌入式软件设计第10次实验报告
  • 原文地址:https://www.cnblogs.com/jackge/p/3114494.html
Copyright © 2011-2022 走看看